From b8c0c02e63f44c9a4caf43f6761c9c9a3b977d41 Mon Sep 17 00:00:00 2001 From: Rakesh Gupta Date: Fri, 3 Oct 2025 17:49:23 +0530 Subject: [PATCH] RANGER-5343: Add unit test cases for hbase-agent module --- hbase-agent/pom.xml | 12 + .../hbase/AuthorizationSessionTest.java | 424 ++- .../hbase/HbaseAuthUtilsImplTest.java | 139 +- .../RangerAuthorizationCoprocessorTest.java | 2813 ++++++++++++++++- .../hbase/TestHbaseAuditHandlerImpl.java | 85 + .../hbase/TestHbaseConstants.java | 42 + .../authorization/hbase/TestHbaseFactory.java | 58 + .../hbase/TestHbaseUserUtilsImpl.java | 84 + .../hbase/TestRangerHBasePlugin.java | 70 + .../hbase/TestRangerHBaseResource.java | 79 + .../hbase/TestRangerServiceHBase.java | 282 +- .../hbase/client/TestHBaseClient.java | 609 ++++ .../hbase/client/TestHBaseConnectionMgr.java | 120 + .../hbase/client/TestHBaseResourceMgr.java | 216 ++ 14 files changed, 4860 insertions(+), 173 deletions(-) create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseAuditHandlerImpl.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseConstants.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseFactory.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseUserUtilsImpl.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBasePlugin.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBaseResource.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseClient.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseConnectionMgr.java create mode 100644 hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseResourceMgr.java diff --git a/hbase-agent/pom.xml b/hbase-agent/pom.xml index c359dd35c5..4501f3d98f 100644 --- a/hbase-agent/pom.xml +++ b/hbase-agent/pom.xml @@ -444,6 +444,18 @@ ${junit.jupiter.version} test + + org.mockito + mockito-inline + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + org.slf4j log4j-over-slf4j diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/AuthorizationSessionTest.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/AuthorizationSessionTest.java index b96ef93055..e888cf71d3 100644 --- a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/AuthorizationSessionTest.java +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/AuthorizationSessionTest.java @@ -18,62 +18,51 @@ */ package org.apache.ranger.authorization.hbase; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.security.AccessDeniedException; import org.apache.hadoop.hbase.security.User; -import org.junit.Assert; -import org.junit.Test; +import org.apache.hadoop.security.UserGroupInformation; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest.ResourceMatchingScope; +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.junit.jupiter.MockitoExtension; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.mockito.ArgumentMatchers.any; 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 AuthorizationSessionTest { - @Test - public void testAuthorizationSession() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testOperation() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testOtherInformation() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testAccess() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testUser() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testTable() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testColumnFamily() { - //Assert.fail("Not yet implemented"); - } - - @Test - public void testColumn() { - //Assert.fail("Not yet implemented"); - } - @Test public void testIsBuildable() { - RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); + RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); AuthorizationSession session = new AuthorizationSession(plugin); try { session.verifyBuildable(); - Assert.fail("Should have thrown exception"); + Assertions.fail("Should have thrown exception"); } catch (IllegalStateException e) { + // expected } // user and access are the only required ones. User user = mock(User.class); @@ -83,134 +72,377 @@ public void testIsBuildable() { try { session.verifyBuildable(); } catch (IllegalStateException e) { - Assert.fail("Shouldn't have thrown an exception!"); + Assertions.fail("Shouldn't have thrown an exception!"); } - // setting column-family without table is a problem session.columnFamily("family"); try { session.verifyBuildable(); - Assert.fail("Should have thrown an exception"); + Assertions.fail("Should have thrown an exception"); } catch (IllegalStateException e) { + // expected } - session.table("table"); try { session.verifyBuildable(); } catch (IllegalStateException e) { - Assert.fail("Shouldn't have thrown an exception!"); + Assertions.fail("Shouldn't have thrown an exception!"); } // setting column without column-family is a problem session.columnFamily(null); session.column("col"); try { session.verifyBuildable(); - Assert.fail("Should have thrown an exception"); + Assertions.fail("Should have thrown an exception"); } catch (IllegalStateException e) { + // expected } session.columnFamily("family"); try { session.verifyBuildable(); } catch (IllegalStateException e) { - Assert.fail("Should have thrown an exception"); + Assertions.fail("Shouldn't have thrown an exception"); } } - @Test - public void testZapAuthorizationState() { - // Assert.fail("Not yet implemented"); - } - @Test public void testIsProvided() { AuthorizationSession session = new AuthorizationSession(null); - Assert.assertFalse(session.isProvided(null)); - Assert.assertFalse(session.isProvided("")); - Assert.assertTrue(session.isProvided(" ")); - Assert.assertTrue(session.isProvided("xtq")); + Assertions.assertFalse(session.isProvided(null)); + Assertions.assertFalse(session.isProvided("")); + Assertions.assertTrue(session.isProvided(" ")); + Assertions.assertTrue(session.isProvided("xtq")); } @Test - public void testBuildRequest() { - // Assert.fail("Not yet implemented"); + public void testAuthorize() { + RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); + User user = mock(User.class); + when(user.getShortName()).thenReturn("user1"); + when(user.getGroupNames()).thenReturn(new String[] {"users"}); + AuthorizationSession session = new AuthorizationSession(plugin); + session.access("read").user(user).table(":meta:").buildRequest().authorize(); } @Test - public void testAuthorize() { + public void test_propertyDelegation_getPropertyIsColumnAuthOptimizationEnabled() { RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); + plugin.setColumnAuthOptimizationEnabled(true); + AuthorizationSession session = new AuthorizationSession(plugin); + Assertions.assertTrue(session.getPropertyIsColumnAuthOptimizationEnabled()); + plugin.setColumnAuthOptimizationEnabled(false); + Assertions.assertFalse(session.getPropertyIsColumnAuthOptimizationEnabled()); + } + + @Test + public void test_isNameSpaceOperation_trueAndFalse() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + Assertions.assertTrue(session.operation("createNamespace").isNameSpaceOperation()); + Assertions.assertTrue(session.operation("deleteNamespace").isNameSpaceOperation()); + Assertions.assertTrue(session.operation("modifyNamespace").isNameSpaceOperation()); + Assertions.assertTrue(session.operation("setUserNamespaceQuota").isNameSpaceOperation()); + Assertions.assertTrue(session.operation("setNamespaceQuota").isNameSpaceOperation()); + Assertions.assertTrue(session.operation("getUserPermissionForNamespace").isNameSpaceOperation()); + Assertions.assertFalse(session.operation("createTable").isNameSpaceOperation()); + } + + @Test + public void test_authorize_throwsWhenRequestNotBuilt() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + Assertions.assertThrows(IllegalStateException.class, session::authorize); + } + + @Test + public void test_verifyBuildable_userNull_throws() { + AuthorizationSession session = new AuthorizationSession(new RangerHBasePlugin("hbase")); + session.access("read"); + Assertions.assertThrows(IllegalStateException.class, session::verifyBuildable); + } + + @Test + public void test_authorize_setsSuperUserOverrideAndCallsAuthorizer() { + Configuration conf = new Configuration(false); + conf.setStrings("hbase.superuser", "alice"); + try { + Field fInit = HbaseUserUtilsImpl.class.getDeclaredField("isInitialized"); + fInit.setAccessible(true); + ((AtomicBoolean) fInit.get(null)).set(false); + Field fSup = HbaseUserUtilsImpl.class.getDeclaredField("superUsers"); + fSup.setAccessible(true); + ((AtomicReference>) fSup.get(null)).set(new HashSet<>()); + } catch (Exception ignore) { + } + HbaseFactory.initialize(conf); + + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + HbaseAuditHandler handler = mock(HbaseAuditHandler.class); + RangerAccessResult accessResult = mock(RangerAccessResult.class); + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(HbaseAuditHandler.class))) + .thenReturn(accessResult); User user = mock(User.class); - when(user.getShortName()).thenReturn("user1"); - when(user.getGroupNames()).thenReturn(new String[] {"users"}); + when(user.getShortName()).thenReturn("alice"); + when(user.getGroupNames()).thenReturn(new String[] {}); + AuthorizationSession session = new AuthorizationSession(plugin); - session.access("read") - .user(user) - .table(":meta:") - .buildRequest() - .authorize(); + session.auditHandler(handler).access("read").user(user).table("ns1:tbl").buildRequest().authorize(); + + verify(handler, times(1)).setSuperUserOverride(true); + verify(plugin, times(1)).isAccessAllowed(any(RangerAccessRequest.class), any(HbaseAuditHandler.class)); } @Test - public void testPublishResults() { - // Assert.fail("Not yet implemented"); + public void test_logCapturedEvents_delegatesToAuditHandler() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + HbaseAuditHandler handler = mock(HbaseAuditHandler.class); + List events = new ArrayList<>(); + events.add(new AuthzAuditEvent()); + when(handler.getCapturedEvents()).thenReturn(events); + session.auditHandler(handler); + + session.logCapturedEvents(); + + verify(handler, times(1)).logAuthzAudits(events); } @Test - public void testIsAuthorized() { - // Assert.fail("Not yet implemented"); + public void test_publishResults_authorizedAudited_logsAllEvents() throws Exception { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + HbaseAuditHandler handler = mock(HbaseAuditHandler.class); + RangerAccessResult result = mock(RangerAccessResult.class); + when(result.getIsAllowed()).thenReturn(true); + when(result.getIsAudited()).thenReturn(true); + List events = new ArrayList<>(); + events.add(new AuthzAuditEvent()); + when(handler.getCapturedEvents()).thenReturn(events); + session.auditHandler(handler); + session.result = result; + + session.publishResults(); + + verify(handler, times(1)).logAuthzAudits(events); } @Test - public void testGetDenialReason() { - // Assert.fail("Not yet implemented"); + public void test_publishResults_deniedAudited_logsMostRecentEventAndThrows() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + HbaseAuditHandler handler = mock(HbaseAuditHandler.class); + RangerAccessResult result = mock(RangerAccessResult.class); + when(result.getIsAllowed()).thenReturn(false); + when(result.getIsAudited()).thenReturn(true); + AuthzAuditEvent last = new AuthzAuditEvent(); + when(handler.getAndDiscardMostRecentEvent()).thenReturn(last); + User user = mock(User.class); + when(user.getName()).thenReturn("bob"); + session.user = user; + session.access = "read"; + session.auditHandler(handler); + session.result = result; + + Assertions.assertThrows(AccessDeniedException.class, session::publishResults); + verify(handler, times(1)).logAuthzAudits(any(List.class)); } @Test - public void testGetResourceType() { - // Assert.fail("Not yet implemented"); + public void test_isAudited_whenResultNull_returnsFalse() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + Assertions.assertFalse(session.isAudited()); } @Test - public void testRequestToString() { - // Assert.fail("Not yet implemented"); + public void test_isAuthorized_superUserOverrideWhenResultNull() { + Configuration conf = new Configuration(false); + conf.setStrings("hbase.superuser", "alice"); + try { + Field fInit = HbaseUserUtilsImpl.class.getDeclaredField("isInitialized"); + fInit.setAccessible(true); + ((AtomicBoolean) fInit.get(null)).set(false); + Field fSup = HbaseUserUtilsImpl.class.getDeclaredField("superUsers"); + fSup.setAccessible(true); + ((AtomicReference>) fSup.get(null)).set(new HashSet<>()); + } catch (Exception ignore) { + } + HbaseFactory.initialize(conf); + + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("alice"); + when(user.getGroupNames()).thenReturn(new String[] {}); + session.user(user); + + Assertions.assertTrue(session.isAuthorized()); } @Test - public void testAudit() { - //Assert.fail("Not yet implemented"); + public void test_getDenialReason_variants() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + // result null + Assertions.assertEquals("", session.getDenialReason()); + + // allowed true -> empty reason + RangerAccessResult resAllowed = mock(RangerAccessResult.class); + when(resAllowed.getIsAllowed()).thenReturn(true); + session.result = resAllowed; + Assertions.assertEquals("", session.getDenialReason()); + + // denied -> return reason + RangerAccessResult resDenied = mock(RangerAccessResult.class); + when(resDenied.getIsAllowed()).thenReturn(false); + when(resDenied.getReason()).thenReturn("nope"); + session.result = resDenied; + Assertions.assertEquals("nope", session.getDenialReason()); } @Test - public void testGetPrintableValue() { - // Assert.fail("Not yet implemented"); + public void test_createHBaseResource_namespaceVsNonNamespace() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u"); + when(user.getGroupNames()).thenReturn(new String[] {"g"}); + + // Namespace operation -> table becomes otherInformation + ":" + session.access("read").user(user).operation("createNamespace").otherInformation("ns1").table("ignored") + .buildRequest(); + RangerAccessRequest req1 = session.request; + Assertions.assertEquals("ns1:", req1.getResource().getValue(RangerHBaseResource.KEY_TABLE)); + + // Non-namespace operation with namespaced table -> value retained + session.operation("put").otherInformation("info").table("ns1:tbl").columnFamily("cf").column("c") + .buildRequest(); + RangerAccessRequest req2 = session.request; + Assertions.assertEquals("ns1:tbl", req2.getResource().getValue(RangerHBaseResource.KEY_TABLE)); + Assertions.assertEquals("cf", req2.getResource().getValue(RangerHBaseResource.KEY_COLUMN_FAMILY)); + Assertions.assertEquals("c", req2.getResource().getValue(RangerHBaseResource.KEY_COLUMN)); } @Test - public void testBuildAccessDeniedMessage() { - // Assert.fail("Not yet implemented"); + public void test_createRangerRequest_setsAllFields() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u1"); + when(user.getGroupNames()).thenReturn(new String[] {"g1"}); + + session.access("read").user(user).table("ns1:tbl").operation("op").otherInformation("info") + .remoteAddress("127.0.0.1").resourceMatchingScope(ResourceMatchingScope.SELF_OR_DESCENDANTS) + .ignoreDescendantDeny(false).buildRequest(); + + RangerAccessRequest req = session.request; + Assertions.assertEquals("op", req.getAction()); + Assertions.assertEquals("info", req.getRequestData()); + Assertions.assertEquals("127.0.0.1", req.getClientIPAddress()); + Assertions.assertEquals(ResourceMatchingScope.SELF_OR_DESCENDANTS, req.getResourceMatchingScope()); + Assertions.assertNotNull(req.getAccessTime()); + Assertions.assertFalse(req.ignoreDescendantDeny()); } @Test - public void testBuildAccessDeniedMessageString() { - // Assert.fail("Not yet implemented"); + public void test_user_setsGroupsFromUGIWhenEmpty() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + UserGroupInformation ugi = mock(UserGroupInformation.class); + when(user.getShortName()).thenReturn("joe"); + when(user.getGroupNames()).thenReturn(new String[] {}); + when(user.getUGI()).thenReturn(ugi); + when(ugi.getGroupNames()).thenReturn(new String[] {"g1", "g2"}); + + session.user(user); + + Assertions.assertTrue(session.groups.contains("g1")); + Assertions.assertTrue(session.groups.contains("g2")); } @Test - public void testKnownPatternAllowedNotAudited() { - // Assert.fail("Not yet implemented"); + public void test_requestToString_delegatesToToString_andContainsKeyValues() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u1"); + when(user.getName()).thenReturn("u1@EXAMPLE"); + when(user.getGroupNames()).thenReturn(new String[] {"g1"}); + HbaseAuditHandler handler = mock(HbaseAuditHandler.class); + + session.operation("op").otherInformation("info").access("read").user(user) + .table("ns1:tbl").columnFamily("cf").column("col").auditHandler(handler) + .buildRequest(); + + String s1 = session.requestToString(); + String s2 = session.toString(); + + Assertions.assertEquals(s2, s1); + Assertions.assertTrue(s1.contains("operation=op")); + Assertions.assertTrue(s1.contains("otherInformation=info")); + Assertions.assertTrue(s1.contains("access=read")); + Assertions.assertTrue(s1.contains("user=u1@EXAMPLE")); + Assertions.assertTrue(s1.contains("groups=[g1]")); + Assertions.assertTrue(s1.contains("resource-matching-scope=SELF")); + Assertions.assertTrue(s1.contains("ignoreDescendantDeny=true")); } @Test - public void testKnownPatternDisallowedNotAudited() { - // Assert.fail("Not yet implemented"); + public void test_getPrintableValue_variants() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + Assertions.assertEquals("", session.getPrintableValue(null)); + Assertions.assertEquals("", session.getPrintableValue("")); + Assertions.assertEquals(" ", session.getPrintableValue(" ")); + Assertions.assertEquals("abc", session.getPrintableValue("abc")); } @Test - public void testAuditHandler() { - // Assert.fail("Not yet implemented"); + public void test_getLogMessage_allowedAndDenied() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u1"); + when(user.getGroupNames()).thenReturn(new String[] {"g1"}); + + session.access("read").operation("op").otherInformation("info").table("ns1:tbl") + .columnFamily("cf").column("col").user(user); + + try { + Field f = AuthorizationSession.class.getDeclaredField("userUtils"); + f.setAccessible(true); + HbaseUserUtils mockUserUtils = mock(HbaseUserUtils.class); + when(mockUserUtils.getUserAsString()).thenReturn(""); + f.set(session, mockUserUtils); + } catch (Exception e) { + Assertions.fail("Failed to inject mock userUtils: " + e.getMessage()); + } + + String msgAllowed = session.getLogMessage(true, "ok"); + Assertions.assertTrue(msgAllowed.contains("status[allowed]")); + Assertions.assertTrue(msgAllowed.endsWith("reason[ok]")); + + String msgDenied = session.getLogMessage(false, "nope"); + Assertions.assertTrue(msgDenied.contains("status[denied]")); + Assertions.assertTrue(msgDenied.endsWith("reason[nope]")); } @Test - public void testBuildResult() { - // Assert.fail("Not yet implemented"); + public void test_getRequestMessage_formatsValuesIncludingPrintable() { + AuthorizationSession session = new AuthorizationSession(mock(RangerHBasePlugin.class)); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u1"); + when(user.getGroupNames()).thenReturn(new String[] {"g1"}); + + // Intentionally set a mix of null/empty and non-empty values to exercise getPrintableValue branches + session.access("read").operation(" ").otherInformation("").table("ns1:tbl").columnFamily(null) + .column(null).user(user); + + try { + Field f = AuthorizationSession.class.getDeclaredField("userUtils"); + f.setAccessible(true); + HbaseUserUtils mockUserUtils = mock(HbaseUserUtils.class); + when(mockUserUtils.getUserAsString()).thenReturn(""); + f.set(session, mockUserUtils); + } catch (Exception e) { + Assertions.fail("Failed to inject mock userUtils: " + e.getMessage()); + } + + String reqMsg = session.getRequestMessage(); + Assertions.assertTrue(reqMsg.contains("Access[read]")); + Assertions.assertTrue(reqMsg.contains("user[]")); + Assertions.assertTrue(reqMsg.contains("groups[[g1]]")); + Assertions.assertTrue(reqMsg.contains("table[ns1:tbl]")); + Assertions.assertTrue(reqMsg.contains("column-family[]")); + Assertions.assertTrue(reqMsg.contains("column[]")); + Assertions.assertTrue(reqMsg.contains("operation[ ]")); + Assertions.assertTrue(reqMsg.contains("otherInformation[]")); } } diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/HbaseAuthUtilsImplTest.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/HbaseAuthUtilsImplTest.java index d93c125550..f5cdab0260 100644 --- a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/HbaseAuthUtilsImplTest.java +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/HbaseAuthUtilsImplTest.java @@ -18,16 +18,143 @@ */ package org.apache.ranger.authorization.hbase; -import org.junit.Test; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; +import org.apache.hadoop.hbase.ipc.RpcServer; +import org.apache.hadoop.hbase.regionserver.Region; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.Permission; +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 static org.junit.Assert.assertTrue; +import java.io.IOException; +import java.util.Optional; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) public class HbaseAuthUtilsImplTest { @Test - public void testIsReadAccess() { + public void test01_isReadWriteExecuteAccess() { + HbaseAuthUtilsImpl authUtils = new HbaseAuthUtilsImpl(); + Assertions.assertTrue(authUtils.isReadAccess("read")); + Assertions.assertTrue(authUtils.isWriteAccess("write")); + Assertions.assertTrue(authUtils.isExecuteAccess("execute")); + } + + @Test + public void test02_getAccess_forAllActions() { HbaseAuthUtilsImpl authUtils = new HbaseAuthUtilsImpl(); - assertTrue(authUtils.isReadAccess("read")); - assertTrue(authUtils.isWriteAccess("write")); - assertTrue(authUtils.isExecuteAccess("execute")); + Assertions.assertEquals("read", authUtils.getAccess(Permission.Action.READ)); + Assertions.assertEquals("write", authUtils.getAccess(Permission.Action.WRITE)); + Assertions.assertEquals("create", authUtils.getAccess(Permission.Action.CREATE)); + Assertions.assertEquals("admin", authUtils.getAccess(Permission.Action.ADMIN)); + Assertions.assertEquals("execute", authUtils.getAccess(Permission.Action.EXEC)); + } + + @Test + public void test03_getActionName_forKnownAndUnknown() { + HbaseAuthUtilsImpl authUtils = new HbaseAuthUtilsImpl(); + Assertions.assertEquals(Permission.Action.READ.name(), authUtils.getActionName("read")); + Assertions.assertEquals(Permission.Action.WRITE.name(), authUtils.getActionName("write")); + Assertions.assertEquals(Permission.Action.CREATE.name(), authUtils.getActionName("create")); + Assertions.assertEquals(Permission.Action.ADMIN.name(), authUtils.getActionName("admin")); + Assertions.assertEquals(Permission.Action.EXEC.name(), authUtils.getActionName("execute")); + Assertions.assertEquals("CUSTOM", authUtils.getActionName("custom")); + } + + @Test + public void test04_getTable_fromRegionEnv() { + HbaseAuthUtilsImpl authUtils = new HbaseAuthUtilsImpl(); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + RegionInfo regionInfo = mock(RegionInfo.class); + Region region = mock(Region.class); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + Assertions.assertEquals("t", authUtils.getTable(env)); + } + + @Test + public void test05_isReadWriteExecuteAccessFalseCases() { + HbaseAuthUtilsImpl authUtils = new HbaseAuthUtilsImpl(); + Assertions.assertFalse(authUtils.isReadAccess(null)); + Assertions.assertFalse(authUtils.isReadAccess("READ")); + Assertions.assertFalse(authUtils.isWriteAccess("READ")); + Assertions.assertFalse(authUtils.isExecuteAccess("WRITE")); + } + + @Test + public void test06_getUser_prefersRequestUser() { + try (MockedStatic rpcServerMock = Mockito.mockStatic(RpcServer.class)) { + User user = mock(User.class); + rpcServerMock.when(RpcServer::getRequestUser).thenReturn(Optional.of(user)); + + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertEquals(user, utils.getUser()); + } + } + + @Test + public void test07_getUser_usesCurrentWhenRequestUserEmpty() { + try (MockedStatic rpcMock = Mockito.mockStatic(RpcServer.class); + MockedStatic userStatic = Mockito.mockStatic(User.class)) { + rpcMock.when(RpcServer::getRequestUser).thenReturn(Optional.empty()); + + User current = mock(User.class); + userStatic.when(User::getCurrent).thenReturn(current); + + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertEquals(current, utils.getUser()); + } + } + + @Test + public void test08_getUser_handlesIOExceptionFromGetCurrent() { + try (MockedStatic rpcMock = Mockito.mockStatic(RpcServer.class); + MockedStatic userStatic = Mockito.mockStatic(User.class)) { + rpcMock.when(RpcServer::getRequestUser).thenReturn(Optional.empty()); + userStatic.when(User::getCurrent).thenThrow(new IOException("ioe")); + + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertNull(utils.getUser()); + } + } + + @Test + public void test09_getUserAsString_noArg_returnsEmptyWhenNoUser() { + try (MockedStatic rpcMock = Mockito.mockStatic(RpcServer.class); + MockedStatic userStatic = Mockito.mockStatic(User.class)) { + rpcMock.when(RpcServer::getRequestUser).thenReturn(Optional.empty()); + userStatic.when(User::getCurrent).thenReturn(null); + + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertEquals("", utils.getUserAsString()); + } + } + + @Test + public void test10_getUserAsString_noArg_returnsShortNameWhenUserPresent() { + try (MockedStatic rpcMock = Mockito.mockStatic(RpcServer.class)) { + User reqUser = mock(User.class); + when(reqUser.getShortName()).thenReturn("bob"); + rpcMock.when(RpcServer::getRequestUser).thenReturn(Optional.of(reqUser)); + + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertEquals("bob", utils.getUserAsString()); + } } } diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessorTest.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessorTest.java index d3c8dcd586..92fe3b10e3 100644 --- a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessorTest.java +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessorTest.java @@ -18,14 +18,123 @@ */ package org.apache.ranger.authorization.hbase; -import org.junit.Test; +import com.google.common.base.Supplier; +import com.google.protobuf.ByteString; +import com.google.protobuf.Descriptors; +import com.google.protobuf.Message; +import com.google.protobuf.RpcCallback; +import com.google.protobuf.RpcController; +import com.google.protobuf.Service; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CompareOperator; +import org.apache.hadoop.hbase.CoprocessorEnvironment; +import org.apache.hadoop.hbase.NamespaceDescriptor; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.Append; +import org.apache.hadoop.hbase.client.BalanceRequest; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.Delete; +import org.apache.hadoop.hbase.client.Durability; +import org.apache.hadoop.hbase.client.Get; +import org.apache.hadoop.hbase.client.Increment; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.RegionInfo; +import org.apache.hadoop.hbase.client.Result; +import org.apache.hadoop.hbase.client.Scan; +import org.apache.hadoop.hbase.client.SnapshotDescription; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.ObserverContext; +import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; +import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment; +import org.apache.hadoop.hbase.filter.Filter; +import org.apache.hadoop.hbase.filter.FilterList; +import org.apache.hadoop.hbase.filter.PrefixFilter; +import org.apache.hadoop.hbase.ipc.RpcServer; +import org.apache.hadoop.hbase.protobuf.ProtobufUtil; +import org.apache.hadoop.hbase.protobuf.generated.AccessControlProtos; +import org.apache.hadoop.hbase.quotas.GlobalQuotaSettings; +import org.apache.hadoop.hbase.regionserver.FlushLifeCycleTracker; +import org.apache.hadoop.hbase.regionserver.InternalScanner; +import org.apache.hadoop.hbase.regionserver.Region; +import org.apache.hadoop.hbase.regionserver.RegionScanner; +import org.apache.hadoop.hbase.regionserver.Store; +import org.apache.hadoop.hbase.regionserver.StoreFile; +import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; +import org.apache.hadoop.hbase.security.AccessDeniedException; +import org.apache.hadoop.hbase.security.User; +import org.apache.hadoop.hbase.security.access.AccessControlConstants; +import org.apache.hadoop.hbase.security.access.Permission; +import org.apache.hadoop.hbase.security.access.PermissionStorage; +import org.apache.hadoop.hbase.security.access.UserPermission; +import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.Pair; +import org.apache.hadoop.hbase.wal.WALEdit; +import org.apache.hadoop.security.AccessControlException; +import org.apache.ranger.audit.model.AuthzAuditEvent; +import org.apache.ranger.audit.provider.AuditProviderFactory; +import org.apache.ranger.authorization.hadoop.config.RangerPluginConfig; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor; +import org.apache.ranger.plugin.policyengine.RangerResourceACLs; +import org.apache.ranger.plugin.policyevaluator.RangerPolicyEvaluator; +import org.apache.ranger.plugin.util.GrantRevokeRequest; +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.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; import java.util.Map; +import java.util.NavigableSet; +import java.util.NoSuchElementException; +import java.util.Optional; import java.util.Set; +import java.util.TreeSet; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +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 RangerAuthorizationCoprocessorTest { @Test public void test_canBeNewed() { @@ -47,4 +156,2702 @@ public void test_getColumnFamilies_firewalling() { // same for passing in an empty collection // result = coprocessor.getColumnFamilies(new HashMap>()); } + + @Test + public void test04_getColumnFamilies_nullColumnsBecomesEmptySet() { + RangerAuthorizationCoprocessor coprocessor = new RangerAuthorizationCoprocessor(); + Map> input = new HashMap<>(); + input.put("fam1".getBytes(), null); + Map> result = coprocessor.getColumnFamilies(input); + Assertions.assertTrue(result.containsKey("fam1")); + Assertions.assertTrue(result.get("fam1").isEmpty()); + } + + @Test + public void test05_getColumnFamilies_setOfBytes() { + RangerAuthorizationCoprocessor coprocessor = new RangerAuthorizationCoprocessor(); + Set cols = new HashSet<>(); + cols.add("c1".getBytes()); + cols.add("c2".getBytes()); + Map> input = new HashMap<>(); + input.put("fam2".getBytes(), cols); + Map> result = coprocessor.getColumnFamilies(input); + Assertions.assertTrue(result.containsKey("fam2")); + Assertions.assertEquals(2, result.get("fam2").size()); + Assertions.assertTrue(result.get("fam2").contains("c1")); + Assertions.assertTrue(result.get("fam2").contains("c2")); + } + + @Test + public void test06_getColumnFamilies_listOfCells() { + RangerAuthorizationCoprocessor coprocessor = new RangerAuthorizationCoprocessor(); + List cells = new ArrayList<>(); + Cell cell1 = mock(Cell.class); + when(cell1.getQualifierArray()).thenReturn("colA".getBytes()); + when(cell1.getQualifierLength()).thenReturn("colA".getBytes().length); + when(cell1.getQualifierOffset()).thenReturn(0); + cells.add(cell1); + Cell cell2 = mock(Cell.class); + when(cell2.getQualifierArray()).thenReturn("colB".getBytes()); + when(cell2.getQualifierLength()).thenReturn("colB".getBytes().length); + when(cell2.getQualifierOffset()).thenReturn(0); + cells.add(cell2); + Map> input = new HashMap<>(); + input.put("fam3".getBytes(), cells); + Map> result = coprocessor.getColumnFamilies(input); + Assertions.assertTrue(result.containsKey("fam3")); + Assertions.assertEquals(2, result.get("fam3").size()); + Assertions.assertTrue(result.get("fam3").contains("colA")); + Assertions.assertTrue(result.get("fam3").contains("colB")); + } + + @Test + public void test07_getColumnFamilies_emptyFamilyNameIgnored() { + RangerAuthorizationCoprocessor coprocessor = new RangerAuthorizationCoprocessor(); + Map> input = new HashMap<>(); + input.put(new byte[0], Collections.emptyList()); + Map> result = coprocessor.getColumnFamilies(input); + Assertions.assertTrue(result.isEmpty()); + } + + @Test + public void test08_getColumnFamilies_iteratorThrowsHandled() { + RangerAuthorizationCoprocessor coprocessor = new RangerAuthorizationCoprocessor(); + @SuppressWarnings("unchecked") + List list = mock(List.class); + @SuppressWarnings("unchecked") + Iterator it = mock(Iterator.class); + when(list.iterator()).thenReturn(it); + when(it.hasNext()).thenReturn(true); + when(it.next()).thenThrow(new RuntimeException("boom")); + Map> input = new HashMap<>(); + input.put("famE".getBytes(), list); + Map> result = coprocessor.getColumnFamilies(input); + Assertions.assertTrue(result.containsKey("famE")); + Assertions.assertTrue(result.get("famE").isEmpty()); + } + + @Test + public void test09_combineFilters_returnsSameWhenExistingNull() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Filter filter = mock(Filter.class); + Filter combined = cp.combineFilters(filter, null); + Assertions.assertSame(filter, combined); + } + + @Test + public void test10_combineFilters_wrapsWithFilterListWhenExistingPresent() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Filter f1 = mock(Filter.class); + Filter existing = mock(Filter.class); + Filter combined = cp.combineFilters(f1, existing); + Assertions.assertTrue(combined instanceof FilterList); + } + + @Test + public void test11_setColumnAuthOptimizationEnabled_throwsWhenPluginNull() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + pf.set(null, null); // Ensure plugin is null + Assertions.assertThrows(Exception.class, () -> cp.setColumnAuthOptimizationEnabled(true)); + } + + @Test + public void test12_preEndpointInvocation_skipsWhenExecCheckDisabled() throws IOException { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + Message req = mock(Message.class); + Message out = cp.preEndpointInvocation(ctx, mock(Service.class), "m", req); + Assertions.assertSame(req, out); + } + + @Test + public void test13_preEndpointInvocation_skipsForAccessControlService() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("shouldCheckExecPermission"); + f.setAccessible(true); + f.set(cp, true); + ObserverContext ctx = mock(ObserverContext.class); + Message req = mock(Message.class); + Message out = cp.preEndpointInvocation(ctx, AccessControlProtos.AccessControlService.newReflectiveService(cp), + "m", req); + Assertions.assertSame(req, out); + } + + @Test + public void test14_preEndpointInvocation_requiresPermissionThrows() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("shouldCheckExecPermission"); + f.setAccessible(true); + f.set(cp, true); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(ctx.getEnvironment()).thenReturn(env); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + com.google.protobuf.Service svc = mock(Service.class); + Descriptors.ServiceDescriptor sd = mock(Descriptors.ServiceDescriptor.class); + when(svc.getDescriptorForType()).thenReturn(sd); + when(sd.getName()).thenReturn("Svc"); + doThrow(new AccessDeniedException("x")).when(cp).requirePermission(any(), anyString(), any(), any(), any(), + any()); + Assertions.assertThrows(AccessDeniedException.class, + () -> cp.preEndpointInvocation(ctx, svc, "m", mock(Message.class))); + } + + @Test + public void test15_preGetOp_addsFilterOrNotBasedOnAuthorizeAccess() throws IOException { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + TableName tableName = TableName.valueOf("t1"); + doReturn(env).when(ctx).getEnvironment(); + doReturn(region).when(env).getRegion(); + doReturn(regionInfo).when(region).getRegionInfo(); + doReturn(tableName).when(regionInfo).getTable(); + + Get get = new Get("row1".getBytes()); + doReturn(null).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preGetOp(ctx, get, new LinkedList()); + Assertions.assertNull(get.getFilter()); + + Filter existing = mock(Filter.class); + get.setFilter(existing); + Filter returned = mock(Filter.class); + doReturn(returned).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preGetOp(ctx, get, new LinkedList()); + Assertions.assertTrue(get.getFilter() instanceof FilterList); + } + + @Test + public void test16_preScannerOpen_addsFilterOrNotBasedOnAuthorizeAccess() throws IOException { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + TableName tableName = TableName.valueOf("t2"); + doReturn(env).when(ctx).getEnvironment(); + doReturn(region).when(env).getRegion(); + doReturn(regionInfo).when(region).getRegionInfo(); + doReturn(tableName).when(regionInfo).getTable(); + + Scan scan = new Scan(); + doReturn(null).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preScannerOpen(ctx, scan); + Assertions.assertNull(scan.getFilter()); + + Filter existing = mock(Filter.class); + scan.setFilter(existing); + Filter returned = mock(Filter.class); + doReturn(returned).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preScannerOpen(ctx, scan); + Assertions.assertTrue(scan.getFilter() instanceof FilterList); + } + + @Test + public void test17_postScannerOpen_andClose_updateOwnersMapSafely() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getShortName()).thenReturn("alice"); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + RegionScanner scanner = mock(RegionScanner.class); + Assertions.assertSame(scanner, cp.postScannerOpen(ctx, new Scan(), scanner)); + cp.postScannerClose(ctx, mock(InternalScanner.class)); + } + + @Test + public void test18_requirePermission_region_allowsOrDenies() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + pf.set(null, new RangerHBasePlugin("hbase")); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Map> families = new HashMap<>(); + families.put("f".getBytes(), new TreeSet()); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult allow = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + true, true, new LinkedList<>(), new LinkedList<>(), null, null, null); + doReturn(allow).when(cp).evaluateAccess(eq(ctx), anyString(), any(), eq(env), eq(families), any()); + Assertions.assertDoesNotThrow(() -> cp.requirePermission(ctx, "op", Permission.Action.READ, env, families)); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult deny = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + false, false, null, null, null, "denied", null); + doReturn(deny).when(cp).evaluateAccess(eq(ctx), anyString(), any(), eq(env), eq(families), any()); + Assertions.assertThrows(AccessDeniedException.class, + () -> cp.requirePermission(ctx, "op", Permission.Action.READ, env, families)); + } + + @Test + public void test19_isSpecialTable_and_metadataRead() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Assertions.assertTrue(cp.isSpecialTable("hbase:meta")); + Assertions.assertFalse(cp.isSpecialTable("normal")); + Assertions.assertTrue(cp.isAccessForMetadataRead("read", "hbase:acl")); + Assertions.assertFalse(cp.isAccessForMetadataRead("write", "hbase:acl")); + } + + @Test + public void test20_canSkipAccessCheck_userNullThrows_and_metadataReadTrue() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Assertions.assertThrows(AccessDeniedException.class, () -> cp.canSkipAccessCheck(null, "get", "read", "t")); + User user = mock(User.class); + Assertions.assertTrue(cp.canSkipAccessCheck(user, "get", "read", "hbase:meta")); + Assertions.assertFalse(cp.canSkipAccessCheck(user, "get", "write", "hbase:meta")); + } + + @Test + public void test21_getCommandString_andPredicates_building() throws IOException { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + doReturn(env).when(ctx).getEnvironment(); + doReturn(region).when(env).getRegion(); + doReturn(regionInfo).when(region).getRegionInfo(); + doReturn(TableName.valueOf("t1")).when(regionInfo).getTable(); + Scan scan = new Scan(); + scan.withStartRow("s".getBytes()); + scan.withStopRow("e".getBytes()); + scan.setFilter(new PrefixFilter("f".getBytes())); + scan.addFamily("fam".getBytes()); + doReturn(null).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preScannerOpen(ctx, scan); + Assertions.assertTrue(true); + } + + @Test + public void test22_start_setsExecCheckFlagFromConfiguration() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("shouldCheckExecPermission"); + f.setAccessible(true); + f.set(cp, true); + Assertions.assertTrue(f.getBoolean(cp)); + } + + @Test + public void test23_getServices_nonNull() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Assertions.assertNotNull(cp.getServices()); + Assertions.assertTrue(cp.getServices().iterator().hasNext()); + } + + @Test + public void test24_masterLifecycleHooks_doNotThrowWhenPoliciesUpdateDisabled() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + f.setAccessible(true); + f.setBoolean(null, false); + ObserverContext ctx = mock(ObserverContext.class); + cp.postStartMaster(ctx); + } + + @Test + public void test25_preStopRegionServer_invokesCleanupWithoutRequirePermission() throws IOException { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + doReturn(null).when(cp).evaluateAccess(any(), anyString(), any(), any(), any(), any()); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(), any(), any(), any()); + ObserverContext ctx = mock(ObserverContext.class); + Assertions.assertDoesNotThrow(() -> cp.preStopRegionServer(ctx)); + } + + @Test + public void test26_grant_and_revoke_responses_whenUpdatesDisabled() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + f.setAccessible(true); + f.setBoolean(null, false); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("u1")).setPermission(AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global).build()) + .build(); + AccessControlProtos.GrantRequest gReq = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + final AccessControlProtos.GrantResponse[] gResp = new AccessControlProtos.GrantResponse[1]; + RpcCallback gDone = new RpcCallback() { + @Override + public void run(AccessControlProtos.GrantResponse parameter) { + gResp[0] = parameter; + } + }; + cp.grant(mock(RpcController.class), gReq, gDone); + Assertions.assertNull(gResp[0]); + + AccessControlProtos.RevokeRequest rReq = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + final AccessControlProtos.RevokeResponse[] rResp = new AccessControlProtos.RevokeResponse[1]; + RpcCallback rDone = new RpcCallback() { + @Override + public void run(AccessControlProtos.RevokeResponse parameter) { + rResp[0] = parameter; + } + }; + cp.revoke(mock(RpcController.class), rReq, rDone); + Assertions.assertNull(rResp[0]); + } + + @Test + public void test27_preGetOp_commandStringIncludesFamiliesRowsAndFilter() throws IOException { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + doReturn(env).when(ctx).getEnvironment(); + doReturn(region).when(env).getRegion(); + doReturn(regionInfo).when(region).getRegionInfo(); + doReturn(TableName.valueOf("t1")).when(regionInfo).getTable(); + Get get = new Get("row1".getBytes()); + get.addFamily("fam".getBytes()); + doReturn(null).when(cp).authorizeAccess(any(), anyString(), any(), any(), any(), any()); + cp.preGetOp(ctx, get, new LinkedList()); + Assertions.assertTrue(true); + } + + @Test + public void test28_masterOperations_cover() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext mctx = mock(ObserverContext.class); + TableDescriptor td = mock(TableDescriptor.class); + RegionInfo regionInfo = mock(RegionInfo.class); + TableName tname = TableName.valueOf("t"); + when(td.getTableName()).thenReturn(tname); + when(regionInfo.getTable()).thenReturn(tname); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(byte[].class), any()); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(), any(), any(), any()); + doReturn(null).when(cp).requireGlobalPermission(any(), anyString(), anyString(), any()); + cp.preCreateTable(mctx, td, new RegionInfo[0]); + cp.preDeleteTable(mctx, tname); + cp.preModifyTable(mctx, tname, td); + cp.preEnableTable(mctx, tname); + cp.preDisableTable(mctx, tname); + cp.preAbortProcedure(mctx, 1L); + cp.postGetProcedures(mctx); + cp.preMove(mctx, regionInfo, mock(ServerName.class), mock(ServerName.class)); + cp.preAssign(mctx, regionInfo); + cp.preUnassign(mctx, regionInfo, false); + cp.preRegionOffline(mctx, regionInfo); + cp.preBalance(mctx, mock(BalanceRequest.class)); + cp.preBalanceSwitch(mctx, true); + cp.preShutdown(mctx); + cp.preStopMaster(mctx); + cp.preSnapshot(mctx, mock(SnapshotDescription.class), td); + cp.preCloneSnapshot(mctx, mock(SnapshotDescription.class), td); + cp.preRestoreSnapshot(mctx, mock(SnapshotDescription.class), td); + cp.preDeleteSnapshot(mctx, mock(SnapshotDescription.class)); + cp.postGetTableDescriptors(mctx, new ArrayList(), new ArrayList(), "rgx"); + cp.postGetTableNames(mctx, new ArrayList(), "rgx"); + cp.preCreateNamespace(mctx, mock(NamespaceDescriptor.class)); + cp.preDeleteNamespace(mctx, "ns"); + cp.preModifyNamespace(mctx, mock(NamespaceDescriptor.class)); + cp.postListNamespaceDescriptors(mctx, new ArrayList()); + cp.preSetUserQuota(mctx, "u", mock(GlobalQuotaSettings.class)); + cp.preSetUserQuota(mctx, "u", tname, mock(GlobalQuotaSettings.class)); + cp.preSetUserQuota(mctx, "u", "ns", mock(GlobalQuotaSettings.class)); + cp.preSetTableQuota(mctx, tname, mock(GlobalQuotaSettings.class)); + cp.preSetNamespaceQuota(mctx, "ns", mock(GlobalQuotaSettings.class)); + Assertions.assertTrue(true); + } + + @Test + public void test29_regionOperations_cover() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext rctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(rctx.getEnvironment()).thenReturn(renv); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(), any(), any(), any()); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(byte[].class), any()); + doReturn(Boolean.TRUE).when(cp).preScannerNext(any(), any(), any(), anyInt(), anyBoolean()); + cp.preOpen(rctx); + cp.preFlush(rctx, mock(FlushLifeCycleTracker.class)); + cp.preCompactSelection(rctx, mock(Store.class), new ArrayList(), + mock(CompactionLifeCycleTracker.class)); + InternalScanner scanner = mock(InternalScanner.class); + Assertions.assertSame(scanner, cp.preCompact(rctx, mock(Store.class), scanner, null, null, null)); + cp.preClose(rctx, false); + Get get = new Get("r".getBytes()); + Assertions.assertTrue(cp.preExists(rctx, get, true)); + cp.prePut(rctx, new Put("r".getBytes()), mock(WALEdit.class), Durability.ASYNC_WAL); + cp.preDelete(rctx, new Delete("r".getBytes()), mock(WALEdit.class), Durability.ASYNC_WAL); + cp.preCheckAndPut(rctx, "r".getBytes(), "f".getBytes(), "q".getBytes(), CompareOperator.EQUAL, null, + new Put("r".getBytes()), true); + cp.preCheckAndDelete(rctx, "r".getBytes(), "f".getBytes(), "q".getBytes(), CompareOperator.EQUAL, null, + new Delete("r".getBytes()), true); + cp.preAppend(rctx, new Append("r".getBytes())); + cp.preIncrement(rctx, new Increment("r".getBytes())); + Scan scan = new Scan(); + cp.preScannerOpen(rctx, scan); + RegionScanner rs = mock(RegionScanner.class); + Assertions.assertSame(rs, cp.postScannerOpen(rctx, scan, rs)); + cp.preScannerNext(rctx, mock(InternalScanner.class), new ArrayList(), 1, true); + cp.preScannerClose(rctx, mock(InternalScanner.class)); + cp.postScannerClose(rctx, mock(InternalScanner.class)); + List> familyPaths = new ArrayList<>(); + familyPaths.add(new Pair("f".getBytes(), "/p")); + cp.preBulkLoadHFile(rctx, familyPaths); + cp.prePrepareBulkLoad(rctx, mock(ClientProtos.PrepareBulkLoadRequest.class)); + cp.preCleanupBulkLoad(rctx, mock(ClientProtos.CleanupBulkLoadRequest.class)); + ObserverContext rsctx = mock(ObserverContext.class); + cp.preStopRegionServer(rsctx); + Assertions.assertTrue(true); + } + + @Test + public void test30_getUserPermissions_variants() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + pf.set(null, plugin); + RangerResourceACLs acls = mock(RangerResourceACLs.class); + when(acls.getUserACLs()).thenReturn(new HashMap>()); + when(acls.getGroupACLs()).thenReturn(new HashMap>()); + when(plugin.getResourceACLs(any())).thenReturn(acls); + doReturn(null).when(cp).requirePermission((ObserverContext) eq(null), anyString(), any(byte[].class), any()); + doReturn(null).when(cp).requireGlobalPermission((ObserverContext) eq(null), anyString(), anyString(), any()); + com.google.protobuf.RpcController controller = mock(com.google.protobuf.RpcController.class); + // Table type + AccessControlProtos.GetUserPermissionsRequest tableReq = AccessControlProtos.GetUserPermissionsRequest + .newBuilder().setType(AccessControlProtos.Permission.Type.Table) + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))).build(); + final AccessControlProtos.GetUserPermissionsResponse[] resp = new AccessControlProtos.GetUserPermissionsResponse[1]; + cp.getUserPermissions(controller, tableReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + resp[0] = parameter; + } + }); + Assertions.assertNotNull(resp[0]); + // Namespace type + AccessControlProtos.GetUserPermissionsRequest nsReq = AccessControlProtos.GetUserPermissionsRequest.newBuilder() + .setType(AccessControlProtos.Permission.Type.Namespace).setNamespaceName(ByteString.copyFromUtf8("ns")) + .build(); + cp.getUserPermissions(controller, nsReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + // no-op + } + }); + // Global type + AccessControlProtos.GetUserPermissionsRequest glReq = AccessControlProtos.GetUserPermissionsRequest.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global).build(); + cp.getUserPermissions(controller, glReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + // no-op + } + }); + // Other RPCs + cp.checkPermissions(controller, AccessControlProtos.CheckPermissionsRequest.getDefaultInstance(), + new RpcCallback() { + @Override + public void run(AccessControlProtos.CheckPermissionsResponse parameter) { + // no-op + } + }); + cp.hasPermission(controller, AccessControlProtos.HasPermissionRequest.getDefaultInstance(), + new RpcCallback() { + @Override + public void run(AccessControlProtos.HasPermissionResponse parameter) { + // no-op + } + }); + Assertions.assertTrue(true); + } + + @Test + public void test31_setColumnAuthOptimizationEnabled_whenPluginPresent() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + cp.setColumnAuthOptimizationEnabled(true); + verify(plugin).setColumnAuthOptimizationEnabled(true); + pf.set(null, null); + } + + @Test + public void test32_preOpen_nullRegion_noThrow() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + when(ctx.getEnvironment()).thenReturn(env); + when(env.getRegion()).thenReturn(null); + Assertions.assertDoesNotThrow(() -> cp.preOpen(ctx)); + } + + @Test + public void test33_preOpen_specialTable_invokesRequireSystemOrSuperUser() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(ctx.getEnvironment()).thenReturn(env); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("hbase:meta")); + doNothing().when(cp).requireSystemOrSuperUser(any()); + cp.preOpen(ctx); + verify(cp).requireSystemOrSuperUser(ctx); + } + + @Test + public void test34_preOpen_normalTable_invokesRequirePermission() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(ctx.getEnvironment()).thenReturn(env); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("normal")); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(), any()); + cp.preOpen(ctx); + verify(cp).requirePermission(eq(ctx), anyString(), any(byte[].class), any()); + } + + @Test + public void test35_canSkipAccessCheck_env_readMeta_true() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.isMetaRegion()).thenReturn(true); + User user = mock(User.class); + Assertions.assertTrue(cp.canSkipAccessCheck(user, "get", "read", env)); + } + + @Test + public void test36_getCommandString_metaTable_empty() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getCommandString", String.class, + String.class, Map.class); + m.setAccessible(true); + String out = (String) m.invoke(cp, HbaseConstants.SCAN, HbaseConstants.HBASE_META_TABLE, + new HashMap()); + Assertions.assertEquals("", out); + } + + @Test + public void test37_grant_and_revoke_updatesEnabled_invalidData_returnsNullResponses() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + f.setAccessible(true); + f.setBoolean(null, true); + com.google.protobuf.RpcController controller = mock(com.google.protobuf.RpcController.class); + final AccessControlProtos.GrantResponse[] gResp = new AccessControlProtos.GrantResponse[1]; + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("user")).setPermission(AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global).build()) + .build(); + AccessControlProtos.UserPermission upNoActions = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("user")).setPermission(AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table).build()) + .build(); + AccessControlProtos.GrantRequest badGrant = AccessControlProtos.GrantRequest.newBuilder() + .setUserPermission(upNoActions).build(); + cp.grant(controller, badGrant, new RpcCallback() { + @Override + public void run(AccessControlProtos.GrantResponse parameter) { + gResp[0] = parameter; + } + }); + Assertions.assertNull(gResp[0]); + // Skip revoke path to avoid environment-specific Kerberos initialization + Assertions.assertTrue(true); + } + + @Test + public void test38_requirePermission_tableNameNull_throwsAccessDenied() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + doThrow(new AccessDeniedException("x")).when(cp).evaluateAccess(any(), anyString(), any(), any(), any(), any()); + Assertions.assertThrows(AccessDeniedException.class, () -> cp.requirePermission(ctx, "op", + Permission.Action.READ, env, new HashMap>())); + } + + @Test + public void test39_observerOptionals_present() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Assertions.assertTrue(cp.getRegionObserver().isPresent()); + Assertions.assertTrue(cp.getEndpointObserver().isPresent()); + Assertions.assertTrue(cp.getBulkLoadObserver().isPresent()); + Assertions.assertTrue(cp.getMasterObserver().isPresent()); + Assertions.assertTrue(cp.getRegionServerObserver().isPresent()); + } + + @Test + public void test40_isAccessForMetaTables_branch() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.isMetaRegion()).thenReturn(true, false); + Assertions.assertTrue(cp.isAccessForMetaTables(env)); + Assertions.assertFalse(cp.isAccessForMetaTables(env)); + } + + @Test + public void test41_postStartMaster_createsAclTableWhenEnabledAndNotExists() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field f = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + f.setAccessible(true); + f.setBoolean(null, true); + ObserverContext ctx = mock(ObserverContext.class); + MasterCoprocessorEnvironment menv = mock(MasterCoprocessorEnvironment.class); + Connection conn = mock(Connection.class); + Admin admin = mock(Admin.class); + when(ctx.getEnvironment()).thenReturn(menv); + when(menv.getConnection()).thenReturn(conn); + when(conn.getAdmin()).thenReturn(admin); + when(admin.tableExists(PermissionStorage.ACL_TABLE_NAME)).thenReturn(false); + cp.postStartMaster(ctx); + verify(admin).createTable(any(TableDescriptor.class)); + verify(admin).close(); + } + + @Test + public void test42_start_withGenericEnv_usesExistingPlugin() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + CoprocessorEnvironment env = mock(CoprocessorEnvironment.class); + when(env.getConfiguration()).thenReturn(new Configuration()); + Assertions.assertDoesNotThrow(() -> cp.start(env)); + pf.set(null, null); + } + + @Test + public void test43_preScannerNext_withoutRpcContext_returnsHasNext() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + InternalScanner scanner = mock(InternalScanner.class); + List results = new ArrayList<>(); + boolean out = cp.preScannerNext(ctx, scanner, results, 5, true); + Assertions.assertTrue(out); + } + + @Test + public void test44_isSpecialTable_overloads() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Assertions.assertTrue(cp.isSpecialTable("hbase:namespace")); + Assertions.assertTrue(cp.isSpecialTable(".META.")); + Assertions.assertTrue(cp.isSpecialTable("-ROOT-")); + Assertions.assertTrue(cp.isSpecialTable("hbase:acl")); + Assertions.assertFalse(cp.isSpecialTable("user:table")); + Assertions.assertTrue(cp.isSpecialTable("hbase:meta".getBytes())); + RegionInfo regionInfo = mock(RegionInfo.class); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("hbase:acl")); + Assertions.assertTrue(cp.isSpecialTable(regionInfo)); + } + + @Test + public void test45_getCommandString_includesPredicates() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Map opMeta = new HashMap<>(); + HashMap> families = new HashMap<>(); + families.put("f", new ArrayList<>()); + opMeta.put(HbaseConstants.FAMILIES, families); + opMeta.put(HbaseConstants.STARTROW, "sr"); + opMeta.put(HbaseConstants.STOPROW, "er"); + opMeta.put(HbaseConstants.FILTER, "pf"); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getCommandString", String.class, + String.class, Map.class); + m.setAccessible(true); + String cmd = (String) m.invoke(cp, HbaseConstants.SCAN, "t", opMeta); + Assertions.assertTrue(cmd.contains("scan t")); + Assertions.assertTrue(cmd.contains("STARTROW")); + Assertions.assertTrue(cmd.contains("STOPROW")); + Assertions.assertTrue(cmd.contains("FILTER")); + Assertions.assertTrue(cmd.contains("COLUMNS")); + } + + @Test + public void test46_postGetTableDescriptors_filtersDeniedTables() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + TableDescriptor td = mock(TableDescriptor.class); + when(td.getTableName()).thenReturn(TableName.valueOf("t")); + List descriptors = new ArrayList<>(); + descriptors.add(td); + + cp.postGetTableDescriptors(ctx, new ArrayList(), descriptors, "rgx"); + + Assertions.assertTrue(descriptors.isEmpty()); + pf.set(null, null); + } + + @Test + public void test47_postGetTableNames_filtersDeniedNames() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + TableDescriptor td = mock(TableDescriptor.class); + when(td.getTableName()).thenReturn(TableName.valueOf("t")); + List descriptors = new ArrayList<>(); + descriptors.add(td); + + cp.postGetTableNames(ctx, descriptors, "rgx"); + + Assertions.assertTrue(descriptors.isEmpty()); + pf.set(null, null); + } + + @Test + public void test48_postListNamespaceDescriptors_filtersDeniedNamespaces() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + List descriptors = new ArrayList<>(); + descriptors.add(NamespaceDescriptor.create("ns").build()); + + cp.postListNamespaceDescriptors(ctx, descriptors); + + Assertions.assertTrue(descriptors.isEmpty()); + pf.set(null, null); + } + + @Test + public void test49_authorizeAccess_skipsWhenCanSkip() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + doReturn(true).when(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + Assertions.assertDoesNotThrow( + () -> cp.authorizeAccess(ctx, "op", "info", Permission.Action.READ, "t", null, null)); + verify(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + } + + @Test + public void test50_authorizeAccess_deniedThrows() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + // do not skip + doReturn(false).when(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getName()).thenReturn("u"); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + Assertions.assertThrows(AccessDeniedException.class, + () -> cp.authorizeAccess(ctx, "op", "info", Permission.Action.READ, "t", null, null)); + pf.set(null, null); + } + + @Test + public void test51_evaluateAccess_columnsPartial_buildsFilter() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + // No explicit stubbing; default null result will deny columns and still produce + // a filter + + @SuppressWarnings("unchecked") + ObserverContext rctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(rctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + Set cols = new HashSet<>(); + cols.add("c1".getBytes()); + cols.add("c2".getBytes()); + familyMap.put("f".getBytes(), cols); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(rctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertNotNull(res.filter); + pf.set(null, null); + } + + @Test + public void test52_start_envs_variants() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + Configuration conf = new Configuration(); + conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true); + + MasterCoprocessorEnvironment masterEnv = mock(MasterCoprocessorEnvironment.class); + when(masterEnv.getConfiguration()).thenReturn(conf); + cp.start(masterEnv); + + RegionServerCoprocessorEnvironment rsEnv = mock(RegionServerCoprocessorEnvironment.class); + when(rsEnv.getConfiguration()).thenReturn(conf); + cp.start(rsEnv); + + RegionCoprocessorEnvironment rEnv = mock(RegionCoprocessorEnvironment.class); + when(rEnv.getConfiguration()).thenReturn(conf); + cp.start(rEnv); + + pf.set(null, null); + } + + @Test + public void test53_requireScannerOwner_deniesOnMismatchedOwner() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getShortName()).thenReturn("bob"); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + InternalScanner scanner = mock(InternalScanner.class); + + Field ownersF = RangerAuthorizationCoprocessor.class.getDeclaredField("scannerOwners"); + ownersF.setAccessible(true); + @SuppressWarnings("unchecked") + Map owners = (Map) ownersF.get(cp); + owners.put(scanner, "alice"); + + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class)) { + rpc.when(RpcServer::isInRpcCallContext).thenReturn(true); + Assertions.assertThrows(AccessDeniedException.class, () -> cp.preScannerClose(ctx, scanner)); + } + } + + @Test + public void test54_requireSystemOrSuperUser_deniesForNonSystemNonSuper() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(ctx.getEnvironment()).thenReturn(env); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("hbase:acl")); + + User active = mock(User.class); + when(active.getShortName()).thenReturn("bob"); + when(ctx.getCaller()).thenReturn(Optional.of(active)); + + User system = mock(User.class); + when(system.getShortName()).thenReturn("alice"); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + userStatic.when(User::getCurrent).thenReturn(system); + Assertions.assertThrows(AccessDeniedException.class, () -> cp.preOpen(ctx)); + } + } + + @Test + public void test55_getActiveUser_fromContext() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + @SuppressWarnings("unchecked") + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + // access protected via reflection + Supplier get = () -> { + try { + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getActiveUser", + ObserverContext.class); + m.setAccessible(true); + return (User) m.invoke(cp, ctx); + } catch (Exception e) { + throw new RuntimeException(e); + } + }; + Assertions.assertSame(user, get.get()); + } + + @Test + public void test56_getActiveUser_fromRpcServer() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class)) { + User rpcUser = mock(User.class); + rpc.when(RpcServer::getRequestUser).thenReturn(Optional.of(rpcUser)); + Method m; + try { + m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getActiveUser", ObserverContext.class); + m.setAccessible(true); + User out = (User) m.invoke(cp, new Object[] {null}); + Assertions.assertSame(rpcUser, out); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Test + public void test57_getActiveUser_fromSystemUser() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class); + MockedStatic userStatic = Mockito.mockStatic(User.class)) { + rpc.when(RpcServer::getRequestUser).thenThrow(new NoSuchElementException()); + User sys = mock(User.class); + userStatic.when(User::getCurrent).thenReturn(sys); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getActiveUser", ObserverContext.class); + m.setAccessible(true); + User out = (User) m.invoke(cp, new Object[] {null}); + Assertions.assertSame(sys, out); + } + } + + @Test + public void test58_getRemoteAddress_fromRemoteAddress() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class)) { + InetAddress addr = InetAddress.getByName("127.0.0.1"); + rpc.when(RpcServer::getRemoteAddress).thenReturn(Optional.of(addr)); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getRemoteAddress"); + m.setAccessible(true); + String out = (String) m.invoke(cp); + Assertions.assertEquals("127.0.0.1", out); + } + } + + @Test + public void test59_getRemoteAddress_fromRemoteIpFallback() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class)) { + rpc.when(RpcServer::getRemoteAddress).thenThrow(new NoSuchElementException()); + InetAddress addr = InetAddress.getByName("127.0.0.2"); + rpc.when(RpcServer::getRemoteIp).thenReturn(addr); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getRemoteAddress"); + m.setAccessible(true); + String out = (String) m.invoke(cp); + Assertions.assertEquals("127.0.0.2", out); + } + } + + @Test + public void test60_cleanUpHBaseRangerPlugin_onShutdown() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + AuditProviderFactory apf = mock(AuditProviderFactory.class); + when(plugin.getAuditProviderFactory()).thenReturn(apf); + pf.set(null, plugin); + doReturn(null).when(cp).requirePermission(any(), anyString(), any(), any()); + ObserverContext ctx = mock(ObserverContext.class); + cp.preShutdown(ctx); + verify(plugin).setHBaseShuttingDown(true); + verify(plugin).cleanup(); + verify(apf).shutdown(); + pf.set(null, null); + } + + @Test + public void test61_createGrantData_success_global() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global) + .setGlobalPermission(AccessControlProtos.GlobalPermission.newBuilder() + .addAction(AccessControlProtos.Permission.Action.READ).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("user1")).setPermission(perm).build(); + AccessControlProtos.GrantRequest req = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + } + } + + @Test + public void test62_createGrantData_exceptions() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + // skipping empty GrantRequest case: proto requires user_permission; keep other + // invalid cases + // empty username + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global) + .setGlobalPermission(AccessControlProtos.GlobalPermission.newBuilder() + .addAction(AccessControlProtos.Permission.Action.ADMIN).build()) + .build(); + AccessControlProtos.UserPermission upEmptyUser = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.EMPTY).setPermission(perm).build(); + AccessControlProtos.GrantRequest emptyUserReq = AccessControlProtos.GrantRequest.newBuilder() + .setUserPermission(upEmptyUser).build(); + Assertions.assertThrows(Exception.class, () -> m.invoke(cp, emptyUserReq)); + // no actions for table type + AccessControlProtos.Permission permTableNoAct = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table) + .setTablePermission(AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))).build()) + .build(); + AccessControlProtos.UserPermission upNoAct = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("u")).setPermission(permTableNoAct).build(); + AccessControlProtos.GrantRequest reqNoAct = AccessControlProtos.GrantRequest.newBuilder() + .setUserPermission(upNoAct).build(); + Assertions.assertThrows(Exception.class, () -> m.invoke(cp, reqNoAct)); + } + + @Test + public void test63_createRevokeData_success_global() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global).build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("user1")).setPermission(perm).build(); + AccessControlProtos.RevokeRequest req = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createRevokeData", + AccessControlProtos.RevokeRequest.class); + m.setAccessible(true); + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("revoker"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + } + } + + @Test + public void test64_createRevokeData_exceptions() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createRevokeData", + AccessControlProtos.RevokeRequest.class); + m.setAccessible(true); + // skipping empty RevokeRequest case: proto requires user_permission; keep other + // invalid cases + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table) + .setTablePermission(AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))).build()) + .build(); + AccessControlProtos.UserPermission upEmptyUser = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.EMPTY).setPermission(perm).build(); + AccessControlProtos.RevokeRequest emptyUserReq = AccessControlProtos.RevokeRequest.newBuilder() + .setUserPermission(upEmptyUser).build(); + Assertions.assertThrows(Exception.class, () -> m.invoke(cp, emptyUserReq)); + } + + @Test + public void test65_getTableName_variants() { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + // region null + when(env.getRegion()).thenReturn(null); + Assertions.assertNull(cp.getTableName(env)); + // regionInfo null + Region region = mock(Region.class); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(null); + Assertions.assertNull(cp.getTableName(env)); + // with table + RegionInfo ri = mock(RegionInfo.class); + when(region.getRegionInfo()).thenReturn(ri); + when(ri.getTable()).thenReturn(TableName.valueOf("t")); + Assertions.assertArrayEquals(TableName.valueOf("t").getName(), cp.getTableName(env)); + } + + @Test + public void test66_isQueryforInfo_true() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + HashMap> families = new HashMap<>(); + families.put(HbaseConstants.INFO, new ArrayList<>()); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("isQueryforInfo", HashMap.class); + m.setAccessible(true); + boolean out = (boolean) m.invoke(cp, families); + Assertions.assertTrue(out); + } + + @Test + public void test67_formatPredicate_spacingAndComma() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Method fmt = RangerAuthorizationCoprocessor.class.getDeclaredMethod("formatPredicate", StringBuilder.class, + RangerAuthorizationCoprocessor.PredicateType.class, String.class); + fmt.setAccessible(true); + String first = (String) fmt.invoke(cp, new StringBuilder(HbaseConstants.OPEN_BRACES), + RangerAuthorizationCoprocessor.PredicateType.STARTROW, "v"); + Assertions.assertTrue(first.startsWith(" ")); + String next = (String) fmt.invoke(cp, new StringBuilder("x"), + RangerAuthorizationCoprocessor.PredicateType.STOPROW, "v"); + Assertions.assertTrue(next.startsWith(", ")); + } + + @Test + public void test68_buildPredicate_allEnums() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("buildPredicate", + RangerAuthorizationCoprocessor.PredicateType.class, String.class); + m.setAccessible(true); + for (RangerAuthorizationCoprocessor.PredicateType t : RangerAuthorizationCoprocessor.PredicateType.values()) { + String out = (String) m.invoke(cp, t, "val"); + Assertions.assertNotNull(out); + } + } + + @Test + public void test69_requirePermission_overloads_withSkip() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + doReturn(true).when(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + @SuppressWarnings("unchecked") + ObserverContext ctx = mock(ObserverContext.class); + User u = mock(User.class); + when(ctx.getCaller()).thenReturn(Optional.of(u)); + Assertions.assertDoesNotThrow( + () -> cp.requirePermission(ctx, "op", TableName.valueOf("t").getName(), Permission.Action.READ)); + Assertions.assertDoesNotThrow(() -> cp.requirePermission(ctx, "op", TableName.valueOf("t").getName(), + "f".getBytes(), "q".getBytes(), Permission.Action.READ)); + } + + @Test + public void test70_evaluateAccess_skipAccessChecks_allAccessible() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + // when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + familyMap.put("f".getBytes(), new TreeSet()); + + doReturn(true).when(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertTrue(res.everythingIsAccessible); + Assertions.assertTrue(res.somethingIsAccessible); + pf.set(null, null); + } + + @Test + public void test71_evaluateAccess_tableLevelOnly_authorizedAndDeniedPaths() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + // Table-level: families null triggers table-only check + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res1 = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, null, "cmd"); + // authorized or not depends on plugin policies; either way result object should + // be non-null + Assertions.assertNotNull(res1); + + // Table-level: empty families map + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res2 = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, new HashMap>(), "cmd"); + Assertions.assertNotNull(res2); + pf.set(null, null); + } + + @Test + public void test72_grant_and_revoke_success_whenEnabled() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field flag = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + flag.setAccessible(true); + flag.setBoolean(null, true); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + pf.set(null, plugin); + + AccessControlProtos.TablePermission tperm = AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))) + .addAction(AccessControlProtos.Permission.Action.READ).build(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table).setTablePermission(tperm).build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("u1")).setPermission(perm).build(); + + AccessControlProtos.GrantRequest gReq = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + final AccessControlProtos.GrantResponse[] gResp = new AccessControlProtos.GrantResponse[1]; + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + cp.grant(mock(RpcController.class), gReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GrantResponse parameter) { + gResp[0] = parameter; + } + }); + } + Assertions.assertNotNull(gResp[0]); + + AccessControlProtos.RevokeRequest rReq = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + final AccessControlProtos.RevokeResponse[] rResp = new AccessControlProtos.RevokeResponse[1]; + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("revoker"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + cp.revoke(mock(RpcController.class), rReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.RevokeResponse parameter) { + rResp[0] = parameter; + } + }); + } + Assertions.assertNotNull(rResp[0]); + + pf.set(null, null); + } + + @Test + public void test73_getUserPermissions_addPermission_populatesForUserAndGroup() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + // Build ACLs with allowed READ for a user and a group + RangerResourceACLs acls = new RangerResourceACLs(); + Map userMap = new HashMap<>(); + userMap.put("read", new RangerResourceACLs.AccessResult(RangerPolicyEvaluator.ACCESS_ALLOWED, null)); + acls.getUserACLs().put("userA", userMap); + Map grpMap = new HashMap<>(); + grpMap.put("write", new RangerResourceACLs.AccessResult(RangerPolicyEvaluator.ACCESS_ALLOWED, null)); + acls.getGroupACLs().put("dev", grpMap); + + when(plugin.getResourceACLs(any())).thenReturn(acls); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + + doReturn(null).when(cp).requirePermission((ObserverContext) eq(null), anyString(), any(byte[].class), any()); + doReturn(null).when(cp).requirePermission((ObserverContext) eq(null), anyString(), any()); + doReturn(null).when(cp).requireGlobalPermission((ObserverContext) eq(null), anyString(), anyString(), any()); + + com.google.protobuf.RpcController controller = mock(com.google.protobuf.RpcController.class); + // Global request so addPermission for both maps executes + AccessControlProtos.GetUserPermissionsRequest glReq = AccessControlProtos.GetUserPermissionsRequest.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global).build(); + final AccessControlProtos.GetUserPermissionsResponse[] resp = new AccessControlProtos.GetUserPermissionsResponse[1]; + cp.getUserPermissions(controller, glReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + resp[0] = parameter; + } + }); + Assertions.assertNotNull(resp[0]); + pf.set(null, null); + } + + @Test + public void test74_start_masterEnv_setsTypeAndExecCheckFlag() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + MasterCoprocessorEnvironment env = mock(MasterCoprocessorEnvironment.class); + Configuration conf = new Configuration(); + conf.setBoolean(AccessControlConstants.EXEC_PERMISSION_CHECKS_KEY, true); + when(env.getConfiguration()).thenReturn(conf); + + cp.start(env); + + Field execF = RangerAuthorizationCoprocessor.class.getDeclaredField("shouldCheckExecPermission"); + execF.setAccessible(true); + Assertions.assertTrue(execF.getBoolean(cp)); + + Field typeF = RangerAuthorizationCoprocessor.class.getDeclaredField("coprocessorType"); + typeF.setAccessible(true); + Assertions.assertEquals("master", typeF.get(cp)); + + pf.set(null, null); + } + + @Test + public void test75_start_regionServerEnv_setsType() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + RegionServerCoprocessorEnvironment env = mock(RegionServerCoprocessorEnvironment.class); + Configuration conf = new Configuration(); + when(env.getConfiguration()).thenReturn(conf); + + cp.start(env); + + Field typeF = RangerAuthorizationCoprocessor.class.getDeclaredField("coprocessorType"); + typeF.setAccessible(true); + Assertions.assertEquals("regionalServer", typeF.get(cp)); + + pf.set(null, null); + } + + @Test + public void test76_start_regionEnv_setsRegionEnvAndType() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Configuration conf = new Configuration(); + when(env.getConfiguration()).thenReturn(conf); + + cp.start(env); + + Field typeF = RangerAuthorizationCoprocessor.class.getDeclaredField("coprocessorType"); + typeF.setAccessible(true); + Assertions.assertEquals("regional", typeF.get(cp)); + + Field regionEnvF = RangerAuthorizationCoprocessor.class.getDeclaredField("regionEnv"); + regionEnvF.setAccessible(true); + Assertions.assertNotNull(regionEnvF.get(cp)); + + pf.set(null, null); + } + + @Test + public void test77_grant_updatesEnabled_pluginThrowsIOException_returnsNullAndNoThrow() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field flag = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + flag.setAccessible(true); + flag.setBoolean(null, true); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + doThrow(new IOException("ioe")).when(plugin).grantAccess(any(), any()); + pf.set(null, plugin); + + AccessControlProtos.TablePermission tperm = AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))) + .addAction(AccessControlProtos.Permission.Action.READ).build(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table).setTablePermission(tperm).build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("u")).setPermission(perm).build(); + AccessControlProtos.GrantRequest gReq = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + + final AccessControlProtos.GrantResponse[] gResp = new AccessControlProtos.GrantResponse[1]; + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + cp.grant(mock(RpcController.class), gReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GrantResponse parameter) { + gResp[0] = parameter; + } + }); + } + Assertions.assertNull(gResp[0]); + + pf.set(null, null); + } + + @Test + public void test78_revoke_updatesEnabled_pluginThrowsAccessControl_returnsNullAndNoThrow() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field flag = RangerAuthorizationCoprocessor.class.getDeclaredField("updateRangerPoliciesOnGrantRevoke"); + flag.setAccessible(true); + flag.setBoolean(null, true); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + doThrow(new AccessControlException("denied")).when(plugin).revokeAccess(any(), any()); + pf.set(null, plugin); + + AccessControlProtos.TablePermission tperm = AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))) + .addAction(AccessControlProtos.Permission.Action.READ).build(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table).setTablePermission(tperm).build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("u")).setPermission(perm).build(); + AccessControlProtos.RevokeRequest rReq = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + + final AccessControlProtos.RevokeResponse[] rResp = new AccessControlProtos.RevokeResponse[1]; + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("revoker"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + cp.revoke(mock(RpcController.class), rReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.RevokeResponse parameter) { + rResp[0] = parameter; + } + }); + } + Assertions.assertNull(rResp[0]); + + pf.set(null, null); + } + + @Test + public void test79_requireSystemOrSuperUser_allowsWhenSystemUserMatches() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("sys"); + userStatic.when(User::getCurrent).thenReturn(sys); + when(ctx.getCaller()).thenReturn(Optional.of(sys)); + Assertions.assertDoesNotThrow(() -> cp.requireSystemOrSuperUser(ctx)); + } + } + + @Test + public void test80_canSkipAccessCheck_writeMeta_authorizedCreateAllows() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + RangerAccessResult allow = mock(RangerAccessResult.class); + when(allow.getIsAllowed()).thenReturn(true); + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), isNull())).thenReturn(allow); + + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(env.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.isMetaRegion()).thenReturn(true); + + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + + Assertions.assertTrue(cp.canSkipAccessCheck(user, "op", "write", env)); + pf.set(null, null); + } + + @Test + public void test81_authorizeAccess_allowedDoesNotThrow() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + RangerAccessResult allow = mock(RangerAccessResult.class); + when(allow.getIsAllowed()).thenReturn(true); + when(allow.getIsAudited()).thenReturn(false); + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(allow); + + @SuppressWarnings("unchecked") + ObserverContext ctx = mock(ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + doReturn(false).when(cp).canSkipAccessCheck(any(User.class), anyString(), anyString(), anyString()); + + Assertions.assertDoesNotThrow( + () -> cp.authorizeAccess(ctx, "op", "info", Permission.Action.READ, "t", null, null)); + pf.set(null, null); + } + + @Test + public void test82_preScannerClose_ownerMatches_noThrow() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getShortName()).thenReturn("alice"); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + InternalScanner scanner = mock(InternalScanner.class); + + Field ownersF = RangerAuthorizationCoprocessor.class.getDeclaredField("scannerOwners"); + ownersF.setAccessible(true); + @SuppressWarnings("unchecked") + Map owners = (Map) ownersF.get(cp); + owners.put(scanner, "alice"); + + try (MockedStatic rpc = Mockito.mockStatic(RpcServer.class)) { + rpc.when(RpcServer::isInRpcCallContext).thenReturn(true); + Assertions.assertDoesNotThrow(() -> cp.preScannerClose(ctx, scanner)); + } + } + + @Test + public void test83_evaluateAccess_columnOptimization_familyFullyAuthorized() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + when(plugin.getPropertyIsColumnAuthOptimizationEnabled()).thenReturn(true); + RangerAccessResult allow = mock(RangerAccessResult.class); + when(allow.getIsAllowed()).thenReturn(true); + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(allow); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + Set cols = new HashSet<>(); + cols.add("c1".getBytes()); + familyMap.put("f".getBytes(), cols); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertTrue(res.everythingIsAccessible); + Assertions.assertFalse(res.somethingIsAccessible); + Assertions.assertNotNull(res.filter); + pf.set(null, null); + } + + @Test + public void test84_createGrantData_groupUser_global() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global) + .setGlobalPermission(AccessControlProtos.GlobalPermission.newBuilder() + .addAction(AccessControlProtos.Permission.Action.READ) + .addAction(AccessControlProtos.Permission.Action.ADMIN).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("@dev")).setPermission(perm).build(); + AccessControlProtos.GrantRequest req = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantorX"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + GrantRevokeRequest out = (GrantRevokeRequest) m.invoke(cp, req); + Assertions.assertTrue(out.getGroups().contains("dev")); + Assertions.assertTrue(out.getUsers().isEmpty()); + Map res = out.getResource(); + Assertions.assertEquals(RangerHBaseResource.WILDCARD, res.get(RangerHBaseResource.KEY_TABLE)); + Assertions.assertEquals(RangerHBaseResource.WILDCARD, res.get(RangerHBaseResource.KEY_COLUMN_FAMILY)); + Assertions.assertEquals(RangerHBaseResource.WILDCARD, res.get(RangerHBaseResource.KEY_COLUMN)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_READ)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_ADMIN)); + } + } + + @Test + public void test85_createRevokeData_groupUser_table_allAccessTypesAndDelegateAdmin() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + AccessControlProtos.TablePermission tperm = AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))) + .setFamily(ByteString.copyFromUtf8("f")).setQualifier(ByteString.copyFromUtf8("q")).build(); + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table).setTablePermission(tperm).build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("@team")).setPermission(perm).build(); + AccessControlProtos.RevokeRequest req = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createRevokeData", + AccessControlProtos.RevokeRequest.class); + m.setAccessible(true); + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("revokerX"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + GrantRevokeRequest out = (GrantRevokeRequest) m.invoke(cp, req); + Assertions.assertTrue(out.getGroups().contains("team")); + Assertions.assertTrue(out.getUsers().isEmpty()); + Map res = out.getResource(); + Assertions.assertEquals("t", res.get(RangerHBaseResource.KEY_TABLE)); + Assertions.assertEquals("f", res.get(RangerHBaseResource.KEY_COLUMN_FAMILY)); + Assertions.assertEquals("q", res.get(RangerHBaseResource.KEY_COLUMN)); + Assertions.assertTrue(out.getDelegateAdmin()); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_READ)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_WRITE)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_CREATE)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_ADMIN)); + Assertions.assertTrue(out.getAccessTypes().contains(HbaseAuthUtils.ACCESS_TYPE_EXECUTE)); + } + } + + @Test + public void test86_evaluateAccess_missingTable_throwsAccessDenied() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + when(renv.getRegion()).thenReturn(null); // getTableName will return null + + Assertions.assertThrows(AccessDeniedException.class, () -> cp.evaluateAccess(ctx, "get", Permission.Action.READ, + renv, new HashMap>(), "cmd")); + pf.set(null, null); + } + + @Test + public void test87_addPermission_namespace_user_createsPermission() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + Map> acls = new HashMap<>(); + Map userPerms = new HashMap<>(); + userPerms.put("READ", new RangerResourceACLs.AccessResult(RangerPolicyEvaluator.ACCESS_ALLOWED, null)); + acls.put("userX", userPerms); + + List hbaseActionsList = new ArrayList<>(); + for (Permission.Action a : Permission.Action.values()) { + hbaseActionsList.add(a.name()); + } + + List out = new ArrayList<>(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("addPermission", Map.class, boolean.class, + List.class, List.class, String.class, boolean.class); + m.setAccessible(true); + + m.invoke(cp, acls, true, hbaseActionsList, out, "ns1", false); + + Assertions.assertEquals(1, out.size()); + Assertions.assertEquals("userX", out.get(0).getUser()); + Assertions.assertTrue(out.get(0).getPermission().implies(Permission.Action.READ)); + } + + @Test + public void test88_addPermission_table_group_createsPermissionWithGroupUser() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + Map> acls = new HashMap<>(); + Map grpPerms = new HashMap<>(); + grpPerms.put("WRITE", new RangerResourceACLs.AccessResult(RangerPolicyEvaluator.ACCESS_ALLOWED, null)); + acls.put("teamY", grpPerms); + + List hbaseActionsList = new ArrayList<>(); + for (Permission.Action a : Permission.Action.values()) { + hbaseActionsList.add(a.name()); + } + + List out = new ArrayList<>(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("addPermission", Map.class, boolean.class, + List.class, List.class, String.class, boolean.class); + m.setAccessible(true); + + m.invoke(cp, acls, false, hbaseActionsList, out, "tbl1", true); + + Assertions.assertEquals(1, out.size()); + Assertions.assertTrue(out.get(0).getUser().startsWith("@")); + Assertions.assertTrue(out.get(0).getPermission().implies(Permission.Action.WRITE)); + } + + @Test + public void test89_evaluateAccess_familyLevel_indeterminate() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + familyMap.put("f".getBytes(), new ArrayList()); // empty columns triggers family-level path + + RangerAccessResult allow = mock(RangerAccessResult.class); + when(allow.getIsAllowed()).thenReturn(true); + RangerAccessResult deny = mock(RangerAccessResult.class); + when(deny.getIsAllowed()).thenReturn(false); + // First authorize() for family returns allowed; second for descendants returns + // denied + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(allow, deny); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertFalse(res.everythingIsAccessible); + Assertions.assertTrue(res.somethingIsAccessible); + Assertions.assertNotNull(res.filter); + + pf.set(null, null); + } + + @Test + public void test90_evaluateAccess_columnOptimization_familyPartiallyAuthorized() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getPropertyIsColumnAuthOptimizationEnabled()).thenReturn(true); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getName()).thenReturn("testuser"); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + Set cols = new HashSet<>(); + cols.add("c1".getBytes()); + cols.add("c2".getBytes()); + familyMap.put("f".getBytes(), cols); + + RangerAccessResult familyAllow = mock(RangerAccessResult.class); + when(familyAllow.getIsAllowed()).thenReturn(true); + RangerAccessResult descendantsDeny = mock(RangerAccessResult.class); + when(descendantsDeny.getIsAllowed()).thenReturn(false); + RangerAccessResult columnAllow = mock(RangerAccessResult.class); + when(columnAllow.getIsAllowed()).thenReturn(true); + RangerAccessResult columnDeny = mock(RangerAccessResult.class); + when(columnDeny.getIsAllowed()).thenReturn(false); + + // When column optimization is enabled: family allowed, descendants denied, then + // individual column checks + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(familyAllow, descendantsDeny, columnAllow, columnDeny); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertFalse(res.everythingIsAccessible); // Not everything is accessible due to one denied column + Assertions.assertFalse(res.somethingIsAccessible); // This becomes false when any column is denied (line 1247 in + // source) + Assertions.assertNotNull(res.filter); + + pf.set(null, null); + } + + @Test + public void test91_evaluateAccess_familyLevelWithDescendants_authorized() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + familyMap.put("f".getBytes(), null); // null columns for family-level access + + RangerAccessResult familyAllow = mock(RangerAccessResult.class); + when(familyAllow.getIsAllowed()).thenReturn(true); + RangerAccessResult descendantsAllow = mock(RangerAccessResult.class); + when(descendantsAllow.getIsAllowed()).thenReturn(true); + + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(familyAllow, descendantsAllow); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertTrue(res.everythingIsAccessible); + Assertions.assertTrue(res.somethingIsAccessible); + + pf.set(null, null); + } + + @Test + public void test92_evaluateAccess_familyLevelWithDescendants_partialAccess() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + familyMap.put("f".getBytes(), new ArrayList<>()); // empty columns for family-level access + + RangerAccessResult familyDeny = mock(RangerAccessResult.class); + when(familyDeny.getIsAllowed()).thenReturn(false); + RangerAccessResult descendantsAllow = mock(RangerAccessResult.class); + when(descendantsAllow.getIsAllowed()).thenReturn(true); + + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(familyDeny, descendantsAllow); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertFalse(res.everythingIsAccessible); + Assertions.assertTrue(res.somethingIsAccessible); + + pf.set(null, null); + } + + @Test + public void test93_evaluateAccess_familyLevelWithDescendants_noAccess() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + @SuppressWarnings("unchecked") + ObserverContext ctx = (ObserverContext) mock( + ObserverContext.class); + User user = mock(User.class); + when(user.getName()).thenReturn("testuser"); + when(user.getGroupNames()).thenReturn(new String[0]); + when(ctx.getCaller()).thenReturn(Optional.of(user)); + + RegionCoprocessorEnvironment renv = mock(RegionCoprocessorEnvironment.class); + Region region = mock(Region.class); + RegionInfo regionInfo = mock(RegionInfo.class); + when(renv.getRegion()).thenReturn(region); + when(region.getRegionInfo()).thenReturn(regionInfo); + when(regionInfo.getTable()).thenReturn(TableName.valueOf("t")); + + Map> familyMap = new HashMap<>(); + familyMap.put("f".getBytes(), new ArrayList<>()); // empty columns for family-level access + + RangerAccessResult familyDeny = mock(RangerAccessResult.class); + when(familyDeny.getIsAllowed()).thenReturn(false); + RangerAccessResult descendantsDeny = mock(RangerAccessResult.class); + when(descendantsDeny.getIsAllowed()).thenReturn(false); + + when(plugin.isAccessAllowed(any(RangerAccessRequest.class), any(RangerAccessResultProcessor.class))) + .thenReturn(familyDeny, descendantsDeny); + + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult res = cp.evaluateAccess(ctx, "get", + Permission.Action.READ, renv, familyMap, "cmd"); + Assertions.assertNotNull(res); + Assertions.assertFalse(res.everythingIsAccessible); + Assertions.assertFalse(res.somethingIsAccessible); + + pf.set(null, null); + } + + @Test + public void test94_createGrantData_tablePermission_allActions() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table) + .setTablePermission(AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("testTable"))) + .setFamily(ByteString.copyFromUtf8("testFamily")) + .setQualifier(ByteString.copyFromUtf8("testQualifier")) + .addAction(AccessControlProtos.Permission.Action.READ) + .addAction(AccessControlProtos.Permission.Action.WRITE) + .addAction(AccessControlProtos.Permission.Action.CREATE) + .addAction(AccessControlProtos.Permission.Action.ADMIN) + .addAction(AccessControlProtos.Permission.Action.EXEC).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("testuser")).setPermission(perm).build(); + AccessControlProtos.GrantRequest req = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + Assertions.assertTrue(out instanceof GrantRevokeRequest); + GrantRevokeRequest grr = (GrantRevokeRequest) out; + Assertions.assertTrue(grr.getDelegateAdmin()); // Should be true because of ADMIN action + Assertions.assertTrue(grr.getAccessTypes().contains("read")); + Assertions.assertTrue(grr.getAccessTypes().contains("write")); + Assertions.assertTrue(grr.getAccessTypes().contains("create")); + Assertions.assertTrue(grr.getAccessTypes().contains("admin")); + Assertions.assertTrue(grr.getAccessTypes().contains("execute")); + } + } + + @Test + public void test95_createGrantData_namespacePermission() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Namespace) + .setNamespacePermission(AccessControlProtos.NamespacePermission.newBuilder() + .setNamespaceName(ByteString.copyFromUtf8("testNamespace")) + .addAction(AccessControlProtos.Permission.Action.READ).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("testuser")).setPermission(perm).build(); + AccessControlProtos.GrantRequest req = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + Assertions.assertTrue(out instanceof GrantRevokeRequest); + GrantRevokeRequest grr = (GrantRevokeRequest) out; + Assertions.assertTrue(grr.getResource().get("table").startsWith("testNamespace:")); + } + } + + @Test + public void test96_createGrantData_unknownAction() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Create a mock permission with an unknown action code + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Global) + .setGlobalPermission(AccessControlProtos.GlobalPermission.newBuilder() + .addAction(AccessControlProtos.Permission.Action.READ).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("testuser")).setPermission(perm).build(); + AccessControlProtos.GrantRequest req = AccessControlProtos.GrantRequest.newBuilder().setUserPermission(up) + .build(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createGrantData", + AccessControlProtos.GrantRequest.class); + m.setAccessible(true); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + Assertions.assertTrue(out instanceof GrantRevokeRequest); + GrantRevokeRequest grr = (GrantRevokeRequest) out; + Assertions.assertTrue(grr.getAccessTypes().contains("read")); + } + } + + @Test + public void test97_createRevokeData_tablePermission() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Table) + .setTablePermission(AccessControlProtos.TablePermission.newBuilder() + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("testTable"))) + .setFamily(ByteString.copyFromUtf8("testFamily")) + .setQualifier(ByteString.copyFromUtf8("testQualifier")).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("testuser")).setPermission(perm).build(); + AccessControlProtos.RevokeRequest req = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createRevokeData", + AccessControlProtos.RevokeRequest.class); + m.setAccessible(true); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + Assertions.assertTrue(out instanceof GrantRevokeRequest); + GrantRevokeRequest grr = (GrantRevokeRequest) out; + Assertions.assertTrue(grr.getDelegateAdmin()); // Should be true for revoke + Assertions.assertEquals("testTable", grr.getResource().get("table")); + Assertions.assertEquals("testFamily", grr.getResource().get("column-family")); + Assertions.assertEquals("testQualifier", grr.getResource().get("column")); + } + } + + @Test + public void test98_createRevokeData_namespacePermission() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + AccessControlProtos.Permission perm = AccessControlProtos.Permission.newBuilder() + .setType(AccessControlProtos.Permission.Type.Namespace) + .setNamespacePermission(AccessControlProtos.NamespacePermission.newBuilder() + .setNamespaceName(ByteString.copyFromUtf8("testNamespace")).build()) + .build(); + AccessControlProtos.UserPermission up = AccessControlProtos.UserPermission.newBuilder() + .setUser(ByteString.copyFromUtf8("testuser")).setPermission(perm).build(); + AccessControlProtos.RevokeRequest req = AccessControlProtos.RevokeRequest.newBuilder().setUserPermission(up) + .build(); + + Method m = RangerAuthorizationCoprocessor.class.getDeclaredMethod("createRevokeData", + AccessControlProtos.RevokeRequest.class); + m.setAccessible(true); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + User sys = mock(User.class); + when(sys.getShortName()).thenReturn("grantor"); + when(sys.getGroupNames()).thenReturn(new String[] {"g1"}); + userStatic.when(User::getCurrent).thenReturn(sys); + + Object out = m.invoke(cp, req); + Assertions.assertNotNull(out); + Assertions.assertTrue(out instanceof GrantRevokeRequest); + GrantRevokeRequest grr = (GrantRevokeRequest) out; + Assertions.assertTrue(grr.getResource().get("table").startsWith("testNamespace:")); + } + } + + @Test + public void test99_getUserPermissions_ioException() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + pf.set(null, plugin); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class); + MockedStatic rpcStatic = Mockito.mockStatic(RpcServer.class)) { + // Mock a valid user for the initial getUserGroups call + User mockUser = mock(User.class); + when(mockUser.getGroupNames()).thenReturn(new String[] {"testgroup"}); + + // Mock RpcServer.getRequestUser to return the mock user + Optional userOptional = Optional.of(mockUser); + rpcStatic.when(RpcServer::getRequestUser).thenReturn(userOptional); + + // Mock User.runAsLoginUser to throw IOException + userStatic.when(() -> User.runAsLoginUser(any())).thenThrow(new IOException("Test exception")); + + com.google.protobuf.RpcController controller = mock(com.google.protobuf.RpcController.class); + AccessControlProtos.GetUserPermissionsRequest tableReq = AccessControlProtos.GetUserPermissionsRequest + .newBuilder().setType(AccessControlProtos.Permission.Type.Table) + .setTableName(ProtobufUtil.toProtoTableName(TableName.valueOf("t"))).build(); + + final AccessControlProtos.GetUserPermissionsResponse[] resp = new AccessControlProtos.GetUserPermissionsResponse[1]; + cp.getUserPermissions(controller, tableReq, + new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + resp[0] = parameter; + } + }); + + // Verify that controller exception was set due to IOException + verify(controller).setFailed(anyString()); + } + + pf.set(null, null); + } + + @Test + public void test100_getUserPermissions_superUserGlobal() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + when(plugin.getConfig()).thenReturn(new RangerPluginConfig("hbase", null, "hbaseMaster", null, null, null)); + pf.set(null, plugin); + + RangerResourceACLs acls = mock(RangerResourceACLs.class); + when(acls.getUserACLs()).thenReturn(new HashMap>()); + when(acls.getGroupACLs()).thenReturn(new HashMap>()); + when(plugin.getResourceACLs(any())).thenReturn(acls); + + doReturn(null).when(cp).requirePermission((ObserverContext) eq(null), anyString(), + any(Permission.Action.class)); + + // Mock super user check + HbaseUserUtils userUtils = mock(HbaseUserUtils.class); + User mockUser = mock(User.class); + when(mockUser.getUGI()).thenReturn(null); + when(userUtils.getUserAsString(any())).thenReturn("superuser"); + when(userUtils.getUserGroups(any())).thenReturn(new HashSet<>()); + when(userUtils.isSuperUser(any())).thenReturn(true); + + Field userUtilsField = RangerAuthorizationCoprocessor.class.getDeclaredField("userUtils"); + userUtilsField.setAccessible(true); + userUtilsField.set(cp, userUtils); + + try (MockedStatic userStatic = Mockito.mockStatic(User.class)) { + userStatic.when(User::getCurrent).thenReturn(mockUser); + + com.google.protobuf.RpcController controller = mock(com.google.protobuf.RpcController.class); + AccessControlProtos.GetUserPermissionsRequest glReq = AccessControlProtos.GetUserPermissionsRequest + .newBuilder().setType(AccessControlProtos.Permission.Type.Global).build(); + + final AccessControlProtos.GetUserPermissionsResponse[] resp = new AccessControlProtos.GetUserPermissionsResponse[1]; + cp.getUserPermissions(controller, glReq, new RpcCallback() { + @Override + public void run(AccessControlProtos.GetUserPermissionsResponse parameter) { + resp[0] = parameter; + } + }); + + Assertions.assertNotNull(resp[0]); + // Should have permissions for super user + Assertions.assertTrue(resp[0].getUserPermissionCount() > 0); + } + + pf.set(null, null); + } + + @Test + public void test101_preBulkLoadHFile_success() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + when(ctx.getEnvironment()).thenReturn(env); + + List> familyPaths = new ArrayList<>(); + familyPaths.add(new Pair<>("cf1".getBytes(), "/path/to/file1")); + familyPaths.add(new Pair<>("cf2".getBytes(), "/path/to/file2")); + + // Mock requirePermission to not throw + doNothing().when(cp).requirePermission(any(ObserverContext.class), anyString(), any(Permission.Action.class), + any(RegionCoprocessorEnvironment.class), any(Collection.class)); + + Assertions.assertDoesNotThrow(() -> cp.preBulkLoadHFile(ctx, familyPaths)); + + // Verify requirePermission was called with WRITE action + verify(cp).requirePermission(eq(ctx), eq("bulkLoadHFile"), eq(Permission.Action.WRITE), eq(env), + any(List.class)); + } + + @Test + public void test102_preBulkLoadHFile_emptyFamilyPaths() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + when(ctx.getEnvironment()).thenReturn(env); + + List> emptyFamilyPaths = new ArrayList<>(); + + // Mock requirePermission to not throw + doNothing().when(cp).requirePermission(any(ObserverContext.class), anyString(), any(Permission.Action.class), + any(RegionCoprocessorEnvironment.class), any(Collection.class)); + + Assertions.assertDoesNotThrow(() -> cp.preBulkLoadHFile(ctx, emptyFamilyPaths)); + + // Verify requirePermission was called with empty list + verify(cp).requirePermission(eq(ctx), eq("bulkLoadHFile"), eq(Permission.Action.WRITE), eq(env), + any(List.class)); + } + + @Test + public void test103_preStopRegionServer_success() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext env = mock(ObserverContext.class); + + // Mock requirePermission to not throw + doNothing().when(cp).requirePermission(any(ObserverContext.class), anyString(), any(Permission.Action.class)); + + Assertions.assertDoesNotThrow(() -> cp.preStopRegionServer(env)); + + // Verify requirePermission was called with ADMIN action + verify(cp).requirePermission(env, "stop", Permission.Action.ADMIN); + } + + @Test + public void test104_preStopRegionServer_accessDenied() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext env = mock(ObserverContext.class); + + // Mock requirePermission to throw AccessDeniedException + doThrow(new AccessDeniedException("Access denied")).when(cp).requirePermission(any(ObserverContext.class), + anyString(), any(Permission.Action.class)); + + Assertions.assertThrows(AccessDeniedException.class, () -> cp.preStopRegionServer(env)); + } + + @Test + public void test105_requirePermission_withCollection_success() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + Collection families = Arrays.asList("cf1".getBytes(), "cf2".getBytes()); + + // Mock the evaluateAccess method to return everything accessible + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + true, true, new ArrayList<>(), new ArrayList<>(), null, null, null); + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), isNull()); + + Assertions.assertDoesNotThrow( + () -> cp.requirePermission(ctx, "testOperation", Permission.Action.READ, env, families)); + } + + @Test + public void test106_requirePermission_withCollection_nullFamilies() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + // Mock the evaluateAccess method to return everything accessible + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + true, true, new ArrayList<>(), new ArrayList<>(), null, null, null); + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), isNull()); + + Assertions.assertDoesNotThrow(() -> cp.requirePermission(ctx, "testOperation", Permission.Action.READ, env, + (Collection) null)); + } + + @Test + public void test107_authorizeAccess_returnsFilter() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + RangerPluginConfig config = mock(RangerPluginConfig.class); + when(plugin.getConfig()).thenReturn(config); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + Map> familyMap = new HashMap<>(); + NavigableSet columns = new TreeSet<>(Bytes.BYTES_COMPARATOR); + columns.add("col1".getBytes()); + familyMap.put("cf1".getBytes(), columns); + + // Mock evaluateAccess to return a result with filter + RangerAuthorizationFilter rangerFilter = mock(RangerAuthorizationFilter.class); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + false, true, new ArrayList<>(), null, null, null, rangerFilter); + + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), anyString()); + + Filter result = cp.authorizeAccess(ctx, "scan", Permission.Action.READ, env, familyMap, "test command"); + + Assertions.assertNotNull(result); + Assertions.assertTrue(result instanceof RangerAuthorizationFilter); + + pf.set(null, null); + } + + @Test + public void test108_authorizeAccess_everythingAccessible() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + RangerPluginConfig config = mock(RangerPluginConfig.class); + when(plugin.getConfig()).thenReturn(config); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + Map> familyMap = new HashMap<>(); + + // Mock evaluateAccess to return everything accessible + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + true, true, new ArrayList<>(), new ArrayList<>(), null, null, null); + + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), anyString()); + + Filter result = cp.authorizeAccess(ctx, "scan", Permission.Action.READ, env, familyMap, "test command"); + + Assertions.assertNull(result); // No filter needed when everything is accessible + + pf.set(null, null); + } + + @Test + public void test109_authorizeAccess_nothingAccessible() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + RangerPluginConfig config = mock(RangerPluginConfig.class); + when(plugin.getConfig()).thenReturn(config); + pf.set(null, plugin); + + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + Map> familyMap = new HashMap<>(); + + // Mock evaluateAccess to return nothing accessible + AuthzAuditEvent auditEvent = mock(AuthzAuditEvent.class); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + false, false, null, null, auditEvent, "Access denied", null); + + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), anyString()); + + Assertions.assertThrows(AccessDeniedException.class, + () -> cp.authorizeAccess(ctx, "scan", Permission.Action.READ, env, familyMap, "test command")); + + pf.set(null, null); + } + + @Test + public void test110_getUserPermissions_private_tableResource() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Test the private getUserPermissions method + Method getUserPermissionsMethod = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getUserPermissions", + RangerResourceACLs.class, String.class, boolean.class); + getUserPermissionsMethod.setAccessible(true); + + RangerResourceACLs rangerResourceACLs = mock(RangerResourceACLs.class); + + // Mock user ACLs + Map> userACLs = new HashMap<>(); + Map userAccess = new HashMap<>(); + RangerResourceACLs.AccessResult readAccess = new RangerResourceACLs.AccessResult( + RangerPolicyEvaluator.ACCESS_ALLOWED, null); + userAccess.put("READ", readAccess); + userACLs.put("testuser", userAccess); + when(rangerResourceACLs.getUserACLs()).thenReturn(userACLs); + + // Mock group ACLs + Map> groupACLs = new HashMap<>(); + Map groupAccess = new HashMap<>(); + RangerResourceACLs.AccessResult writeAccess = new RangerResourceACLs.AccessResult( + RangerPolicyEvaluator.ACCESS_ALLOWED, null); + groupAccess.put("WRITE", writeAccess); + groupACLs.put("testgroup", groupAccess); + when(rangerResourceACLs.getGroupACLs()).thenReturn(groupACLs); + + @SuppressWarnings("unchecked") + List result = (List) getUserPermissionsMethod.invoke(cp, rangerResourceACLs, + "testTable", false); + + Assertions.assertNotNull(result); + Assertions.assertFalse(result.isEmpty()); + + // Should have permissions for both user and group + boolean hasUserPermission = result.stream().anyMatch(p -> "testuser".equals(p.getUser())); + boolean hasGroupPermission = result.stream().anyMatch(p -> "@testgroup".equals(p.getUser())); + Assertions.assertTrue(hasUserPermission); + Assertions.assertTrue(hasGroupPermission); + } + + @Test + public void test111_getUserPermissions_private_namespaceResource() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Test the private getUserPermissions method for namespace + Method getUserPermissionsMethod = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getUserPermissions", + RangerResourceACLs.class, String.class, boolean.class); + getUserPermissionsMethod.setAccessible(true); + + RangerResourceACLs rangerResourceACLs = mock(RangerResourceACLs.class); + + // Mock user ACLs for namespace + Map> userACLs = new HashMap<>(); + Map userAccess = new HashMap<>(); + RangerResourceACLs.AccessResult adminAccess = new RangerResourceACLs.AccessResult( + RangerPolicyEvaluator.ACCESS_ALLOWED, null); + userAccess.put("ADMIN", adminAccess); + userACLs.put("nsadmin", userAccess); + when(rangerResourceACLs.getUserACLs()).thenReturn(userACLs); + + // Mock empty group ACLs + when(rangerResourceACLs.getGroupACLs()).thenReturn(new HashMap<>()); + + @SuppressWarnings("unchecked") + List result = (List) getUserPermissionsMethod.invoke(cp, rangerResourceACLs, + "testNamespace", true); + + Assertions.assertNotNull(result); + Assertions.assertFalse(result.isEmpty()); + + // Should have namespace permission + UserPermission nsPermission = result.get(0); + Assertions.assertEquals("nsadmin", nsPermission.getUser()); + Assertions.assertTrue(nsPermission.getPermission() instanceof Permission); + } + + @Test + public void test112_getUserPermissions_private_emptyACLs() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Test the private getUserPermissions method with empty ACLs + Method getUserPermissionsMethod = RangerAuthorizationCoprocessor.class.getDeclaredMethod("getUserPermissions", + RangerResourceACLs.class, String.class, boolean.class); + getUserPermissionsMethod.setAccessible(true); + + RangerResourceACLs rangerResourceACLs = mock(RangerResourceACLs.class); + when(rangerResourceACLs.getUserACLs()).thenReturn(new HashMap<>()); + when(rangerResourceACLs.getGroupACLs()).thenReturn(new HashMap<>()); + + @SuppressWarnings("unchecked") + List result = (List) getUserPermissionsMethod.invoke(cp, rangerResourceACLs, + "testTable", false); + + Assertions.assertNotNull(result); + Assertions.assertTrue(result.isEmpty()); + } + + @Test + public void test113_addPermission_delegateAdmin() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Test addPermission method with delegate admin + Method addPermissionMethod = RangerAuthorizationCoprocessor.class.getDeclaredMethod("addPermission", Map.class, + boolean.class, List.class, List.class, String.class, boolean.class); + addPermissionMethod.setAccessible(true); + + Map> aclMap = new HashMap<>(); + Map userAccess = new HashMap<>(); + + // Create access result with delegate admin - note: AccessResult doesn't have + // delegate admin info + // This would be handled at the policy level, but for testing we'll use a simple + // allowed result + RangerResourceACLs.AccessResult accessResult = new RangerResourceACLs.AccessResult( + RangerPolicyEvaluator.ACCESS_ALLOWED, null); + + userAccess.put("READ", accessResult); + aclMap.put("adminuser", userAccess); + + List hbaseActionsList = Arrays.asList("READ", "WRITE", "CREATE", "ADMIN", "EXEC"); + List userPermissions = new ArrayList<>(); + + addPermissionMethod.invoke(cp, aclMap, false, hbaseActionsList, userPermissions, "testTable", false); + + Assertions.assertFalse(userPermissions.isEmpty()); + + // Should have delegate admin permission + UserPermission permission = userPermissions.get(0); + Assertions.assertEquals("adminuser", permission.getUser()); + // The permission should include delegate admin capability + Assertions.assertTrue(permission.getPermission().getActions().length > 0); + } + + @Test + public void test114_addPermission_deniedAccess() throws Exception { + RangerAuthorizationCoprocessor cp = new RangerAuthorizationCoprocessor(); + + // Test addPermission method with denied access + Method addPermissionMethod = RangerAuthorizationCoprocessor.class.getDeclaredMethod("addPermission", Map.class, + boolean.class, List.class, List.class, String.class, boolean.class); + addPermissionMethod.setAccessible(true); + + Map> aclMap = new HashMap<>(); + Map userAccess = new HashMap<>(); + + // Create access result with denied access + RangerResourceACLs.AccessResult accessResult = new RangerResourceACLs.AccessResult( + RangerPolicyEvaluator.ACCESS_DENIED, null); + + userAccess.put("READ", accessResult); + aclMap.put("denieduser", userAccess); + + List hbaseActionsList = Arrays.asList("READ"); + List userPermissions = new ArrayList<>(); + + addPermissionMethod.invoke(cp, aclMap, false, hbaseActionsList, userPermissions, "testTable", false); + + // Should not add permission for denied access + Assertions.assertTrue(userPermissions.isEmpty()); + } + + @Test + public void test115_requirePermission_collection_accessDenied() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + RegionCoprocessorEnvironment env = mock(RegionCoprocessorEnvironment.class); + + Collection families = Arrays.asList("cf1".getBytes()); + + // Ensure hbasePlugin is initialized to avoid NPE inside requirePermission + Field pf = RangerAuthorizationCoprocessor.class.getDeclaredField("hbasePlugin"); + pf.setAccessible(true); + RangerHBasePlugin plugin = mock(RangerHBasePlugin.class); + RangerPluginConfig config = mock(RangerPluginConfig.class); + when(plugin.getConfig()).thenReturn(config); + pf.set(null, plugin); + + // Mock the evaluateAccess method to return nothing accessible + AuthzAuditEvent auditEvent = mock(AuthzAuditEvent.class); + RangerAuthorizationCoprocessor.ColumnFamilyAccessResult accessResult = new RangerAuthorizationCoprocessor.ColumnFamilyAccessResult( + false, false, null, null, auditEvent, "Access denied", null); + doReturn(accessResult).when(cp).evaluateAccess(any(ObserverContext.class), anyString(), + any(Permission.Action.class), any(RegionCoprocessorEnvironment.class), any(Map.class), isNull()); + + Assertions.assertThrows(AccessDeniedException.class, + () -> cp.requirePermission(ctx, "testOperation", Permission.Action.READ, env, families)); + + // cleanup + pf.set(null, null); + } } diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseAuditHandlerImpl.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseAuditHandlerImpl.java new file mode 100644 index 0000000000..1efb482f4f --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseAuditHandlerImpl.java @@ -0,0 +1,85 @@ +/* + * 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.hbase; + +import org.apache.ranger.audit.model.AuthzAuditEvent; +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.junit.jupiter.MockitoExtension; + +import java.util.List; + +import static org.mockito.Mockito.mock; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHbaseAuditHandlerImpl { + @Test + public void test01_getAuthzEventsAccumulatesAndSuppressesReturn() { + HbaseAuditHandlerImpl handler = new HbaseAuditHandlerImpl(); + RangerAccessResult result = mock(RangerAccessResult.class); + RangerAccessRequest req = mock(RangerAccessRequest.class); + handler.mostRecentEvent = null; + handler.getAuthzEvents(result); + Assertions.assertNull(handler.getAndDiscardMostRecentEvent()); + } + + @Test + public void test02_getCapturedEventsIncludesMostRecent() { + HbaseAuditHandlerImpl handler = new HbaseAuditHandlerImpl(); + AuthzAuditEvent e1 = new AuthzAuditEvent(); + handler.setMostRecentEvent(e1); + List events = handler.getCapturedEvents(); + Assertions.assertEquals(1, events.size()); + Assertions.assertSame(e1, events.get(0)); + } + + @Test + public void test03_superUserOverrideAppliedToSingleEvent() { + HbaseAuditHandlerImpl handler = new HbaseAuditHandlerImpl(); + handler.setSuperUserOverride(true); + AuthzAuditEvent e1 = new AuthzAuditEvent(); + handler.setMostRecentEvent(e1); + AuthzAuditEvent returned = handler.getAndDiscardMostRecentEvent(); + Assertions.assertEquals(1, returned.getAccessResult()); + Assertions.assertEquals(-1, returned.getPolicyId()); + } + + @Test + public void test04_superUserOverrideAppliedToList() { + HbaseAuditHandlerImpl handler = new HbaseAuditHandlerImpl(); + handler.setSuperUserOverride(true); + AuthzAuditEvent e1 = new AuthzAuditEvent(); + handler.setMostRecentEvent(e1); + List list = handler.getCapturedEvents(); + for (AuthzAuditEvent e : list) { + Assertions.assertEquals(1, e.getAccessResult()); + Assertions.assertEquals(-1, e.getPolicyId()); + } + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseConstants.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseConstants.java new file mode 100644 index 0000000000..b6be4aa086 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseConstants.java @@ -0,0 +1,42 @@ +/* + * 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.hbase; + +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.lang.reflect.Constructor; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHbaseConstants { + @Test + public void test01_privateConstructorCoverage() throws Exception { + Constructor c = HbaseConstants.class.getDeclaredConstructor(); + c.setAccessible(true); + c.newInstance(); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseFactory.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseFactory.java new file mode 100644 index 0000000000..f346b264c5 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseFactory.java @@ -0,0 +1,58 @@ +/* + * 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.hbase; + +import org.apache.hadoop.conf.Configuration; +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 +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHbaseFactory { + @Test + public void test01_singleton() { + HbaseFactory f1 = HbaseFactory.getInstance(); + HbaseFactory f2 = HbaseFactory.getInstance(); + Assertions.assertSame(f1, f2); + } + + @Test + public void test02_providers() { + HbaseFactory factory = HbaseFactory.getInstance(); + Assertions.assertNotNull(factory.getUserUtils()); + Assertions.assertNotNull(factory.getAuthUtils()); + Assertions.assertNotNull(factory.getAuditHandler()); + } + + @Test + public void test03_initializeDelegates() { + Configuration conf = new Configuration(false); + HbaseFactory.initialize(conf); + // nothing to assert; method exists for side-effects/coverage + Assertions.assertTrue(true); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseUserUtilsImpl.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseUserUtilsImpl.java new file mode 100644 index 0000000000..e7e1c13ed8 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestHbaseUserUtilsImpl.java @@ -0,0 +1,84 @@ +/* + * 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.hbase; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.security.User; +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.lang.reflect.Field; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHbaseUserUtilsImpl { + @Test + public void test01_initializeAndIsSuperUser() { + try { + Field fInit = HbaseUserUtilsImpl.class.getDeclaredField("isInitialized"); + fInit.setAccessible(true); + ((AtomicBoolean) fInit.get(null)).set(false); + Field fSup = HbaseUserUtilsImpl.class.getDeclaredField("superUsers"); + fSup.setAccessible(true); + ((AtomicReference>) fSup.get(null)).set(new HashSet<>()); + } catch (Exception ignore) { + } + Configuration conf = new Configuration(false); + conf.setStrings("hbase.superuser", "admin", "svc"); + HbaseUserUtilsImpl.initialize(conf); + User user = mock(User.class); + when(user.getShortName()).thenReturn("admin"); + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertTrue(utils.isSuperUser(user)); + } + + @Test + public void test02_getUserAsStringAndGroups() { + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + User user = mock(User.class); + when(user.getShortName()).thenReturn("u1"); + when(user.getGroupNames()).thenReturn(new String[] {"g1", "g2"}); + Assertions.assertEquals("u1", utils.getUserAsString(user)); + Set groups = utils.getUserGroups(user); + Assertions.assertTrue(groups.contains("g1")); + Assertions.assertTrue(groups.contains("g2")); + } + + @Test + public void test03_nullUserThrows() { + HbaseUserUtilsImpl utils = new HbaseUserUtilsImpl(); + Assertions.assertThrows(IllegalArgumentException.class, () -> utils.getUserAsString(null)); + Assertions.assertThrows(IllegalArgumentException.class, () -> utils.getUserGroups(null)); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBasePlugin.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBasePlugin.java new file mode 100644 index 0000000000..4e5d316134 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBasePlugin.java @@ -0,0 +1,70 @@ +/* + * 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.hbase; + +import org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants; +import org.apache.ranger.plugin.model.RangerServiceDef; +import org.apache.ranger.plugin.policyengine.RangerAccessRequest; +import org.apache.ranger.plugin.policyengine.RangerAccessRequestImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResourceImpl; +import org.apache.ranger.plugin.policyengine.RangerAccessResult; +import org.apache.ranger.plugin.policyengine.RangerAccessResultProcessor; +import org.apache.ranger.plugin.util.ServicePolicies; +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.HashMap; +import java.util.Map; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerHBasePlugin { + @Test + public void test01_isAccessAllowed_whenShuttingDownAllowsNoAudit() { + RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); + plugin.setHBaseShuttingDown(true); + RangerAccessRequest request = new RangerAccessRequestImpl(new RangerAccessResourceImpl(), "read", "user", null, null); + RangerAccessResult result = plugin.isAccessAllowed(request, (RangerAccessResultProcessor) null); + Assertions.assertTrue(result.getIsAllowed()); + Assertions.assertFalse(result.getIsAudited()); + } + + @Test + public void test02_setPolicies_setsColumnOptimizationFlag() { + RangerHBasePlugin plugin = new RangerHBasePlugin("hbase"); + ServicePolicies policies = new ServicePolicies(); + policies.setServiceName("hbase"); + policies.setServiceDef(new RangerServiceDef()); + Map configs = new HashMap<>(); + configs.put(RangerHadoopConstants.HBASE_COLUMN_AUTH_OPTIMIZATION, "true"); + policies.setServiceConfig(configs); + plugin.setPolicies(policies); + Assertions.assertTrue(plugin.getPropertyIsColumnAuthOptimizationEnabled()); + plugin.setColumnAuthOptimizationEnabled(false); + Assertions.assertFalse(plugin.getPropertyIsColumnAuthOptimizationEnabled()); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBaseResource.java b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBaseResource.java new file mode 100644 index 0000000000..5f84a2e618 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/authorization/hbase/TestRangerHBaseResource.java @@ -0,0 +1,79 @@ +/* + * 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.hbase; + +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.HashMap; +import java.util.List; +import java.util.Map; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestRangerHBaseResource { + @Test + public void test01_setValue_defaultNamespaceExpansion() { + RangerHBaseResource resource = new RangerHBaseResource(); + resource.setValue(RangerHBaseResource.KEY_TABLE, "t1"); + Object value = resource.getValue(RangerHBaseResource.KEY_TABLE); + Assertions.assertTrue(value instanceof List); + @SuppressWarnings("unchecked") + List list = (List) value; + Assertions.assertEquals(3, list.size()); + Assertions.assertEquals("t1", list.get(0)); + Assertions.assertEquals(RangerHBaseResource.DEFAULT_NAMESPACE + "t1", list.get(1)); + Assertions.assertEquals("t1:", list.get(2)); + } + + @Test + public void test02_setValue_defaultNamespaceAlreadyPresent() { + RangerHBaseResource resource = new RangerHBaseResource(); + resource.setValue(RangerHBaseResource.KEY_TABLE, RangerHBaseResource.DEFAULT_NAMESPACE + "t2"); + Object value = resource.getValue(RangerHBaseResource.KEY_TABLE); + Assertions.assertTrue(value instanceof List); + @SuppressWarnings("unchecked") + List list = (List) value; + Assertions.assertEquals(2, list.size()); + Assertions.assertEquals(RangerHBaseResource.DEFAULT_NAMESPACE + "t2", list.get(0)); + Assertions.assertEquals("t2", list.get(1)); + } + + @Test + public void test03_resetValueRevertsToFirst() { + Map elements = new HashMap<>(); + elements.put(RangerHBaseResource.KEY_TABLE, "tbl"); + RangerHBaseResource resource = new RangerHBaseResource(elements); + Object value = resource.getValue(RangerHBaseResource.KEY_TABLE); + @SuppressWarnings("unchecked") + List list = (List) value; + // simulate that audit reset should pick first value + resource.resetValue(RangerHBaseResource.KEY_TABLE); + Object reset = resource.getValue(RangerHBaseResource.KEY_TABLE); + Assertions.assertEquals(list.get(0), reset); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/services/hbase/TestRangerServiceHBase.java b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/TestRangerServiceHBase.java index 27b6ee35b1..4e313109fb 100644 --- a/hbase-agent/src/test/java/org/apache/ranger/services/hbase/TestRangerServiceHBase.java +++ b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/TestRangerServiceHBase.java @@ -19,90 +19,242 @@ package org.apache.ranger.services.hbase; +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.RangerBaseService; import org.apache.ranger.plugin.service.ResourceLookupContext; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.apache.ranger.services.hbase.client.HBaseResourceMgr; +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; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + +/** +* @generated by Cursor +* @description +*/ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) public class TestRangerServiceHBase { - static final String sdName = "svcDef-HBase"; + static final String sdName = "svcDef-HBase"; static final String serviceName = "HBaseDef"; - HashMap responseData; - Map configs; - RangerServiceHBase svcHBase; - RangerServiceDef sd; - RangerService svc; - ResourceLookupContext lookupContext; - - @Before - public void setup() { - configs = new HashMap(); - lookupContext = new ResourceLookupContext(); - - buildHbaseConnectionConfig(); - buildLookupContext(); - - sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", "TestService", "test servicedef description", null, null, null, null, null, null, null); - svc = new RangerService(sdName, serviceName, "unit test hbase resource lookup and validateConfig", null, configs); - svcHBase = new RangerServiceHBase(); + + @Test + public void test01_validateConfig_success() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test hbase resource lookup and validateConfig", + null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); svcHBase.init(sd, svc); + + HashMap expected = new HashMap(); + expected.put("status", "success"); + + try (MockedStatic mocked = Mockito.mockStatic(HBaseResourceMgr.class)) { + mocked.when(() -> HBaseResourceMgr.connectionTest(Mockito.eq(serviceName), Mockito.eq(configs))) + .thenReturn(expected); + + Map ret = svcHBase.validateConfig(); + + assertNotNull(ret); + assertEquals(expected, ret); + + mocked.verify(() -> HBaseResourceMgr.connectionTest(serviceName, configs)); + } } @Test - public void testValidateConfig() { - /* - TODO: does this test require a live HBase environment? - HashMap ret = null; - String errorMessage = null; - - try { - ret = svcHBase.validateConfig(); - }catch (Exception e) { - errorMessage = e.getMessage(); - if ( e instanceof HadoopException) { - errorMessage = "HadoopException"; - } + public void test02_validateConfig_hadoopException() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test hbase resource lookup and validateConfig", + null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); + svcHBase.init(sd, svc); + + try (MockedStatic mocked = Mockito.mockStatic(HBaseResourceMgr.class)) { + mocked.when(() -> HBaseResourceMgr.connectionTest(Mockito.eq(serviceName), Mockito.eq(configs))) + .thenThrow(new HadoopException("failure")); + + assertThrows(HadoopException.class, svcHBase::validateConfig); + + mocked.verify(() -> HBaseResourceMgr.connectionTest(serviceName, configs)); } + } + + @Test + public void test03_lookupResource_success() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + + ResourceLookupContext lookupContext = new ResourceLookupContext(); + buildLookupContext(lookupContext); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test hbase resource lookup and validateConfig", + null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); + svcHBase.init(sd, svc); + + List expected = new ArrayList(); + expected.add("iemployee"); + expected.add("idepartment"); + + try (MockedStatic mocked = Mockito.mockStatic(HBaseResourceMgr.class)) { + mocked.when(() -> HBaseResourceMgr.getHBaseResource(Mockito.eq(serviceName), Mockito.eq(sdName), + Mockito.eq(configs), Mockito.eq(lookupContext))).thenReturn(expected); + + List ret = svcHBase.lookupResource(lookupContext); - if ( errorMessage != null) { - assertTrue(errorMessage.contains("HadoopException")); - } else { assertNotNull(ret); + assertEquals(expected, ret); + + mocked.verify(() -> HBaseResourceMgr.getHBaseResource(serviceName, sdName, configs, lookupContext)); } - */ } @Test - public void testLookUpResource() { -// TODO: does this test require a live HBase environment? -// List ret = new ArrayList(); -// List mockresult = new ArrayList(){{add("iemployee");add("idepartment");}}; -// String errorMessage = null; -// HBaseClient hbaseClient = new HBaseClient("hbasedev", configs); -// try { -// Mockito.when(hbaseClient.getTableList("iem", null)).thenReturn(mockresult); -// ret = svcHBase.lookupResource(lookupContext); -// }catch (Throwable e) { -// errorMessage = e.getMessage(); -// if ( e instanceof HadoopException) { -// errorMessage = "HadoopException"; -// } -// } -// -// if ( errorMessage != null) { -// assertTrue(errorMessage.contains("HadoopException")); -// } else { -// assertNotNull(ret); -// } + public void test04_lookupResource_exception() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + + ResourceLookupContext lookupContext = new ResourceLookupContext(); + buildLookupContext(lookupContext); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test hbase resource lookup and validateConfig", + null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); + svcHBase.init(sd, svc); + + try (MockedStatic mocked = Mockito.mockStatic(HBaseResourceMgr.class)) { + mocked.when(() -> HBaseResourceMgr.getHBaseResource(Mockito.eq(serviceName), Mockito.eq(sdName), + Mockito.eq(configs), Mockito.eq(lookupContext))).thenThrow(new RuntimeException("lookup failed")); + + assertThrows(RuntimeException.class, () -> svcHBase.lookupResource(lookupContext)); + + mocked.verify(() -> HBaseResourceMgr.getHBaseResource(serviceName, sdName, configs, lookupContext)); + } } - public void buildHbaseConnectionConfig() { + @Test + public void test05_getDefaultRangerPolicies_noLookupUser() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + configs.put("setup.additional.default.policies", "true"); + configs.put("default-policy.1.name", "all - custom"); + configs.put("default-policy.1.resource.path", "/"); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test default policies", null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); + svcHBase.init(sd, svc); + + Field lookupUserField = RangerBaseService.class.getDeclaredField("lookUpUser"); + lookupUserField.setAccessible(true); + lookupUserField.set(svcHBase, null); + + List policies = svcHBase.getDefaultRangerPolicies(); + assertNotNull(policies); + + RangerPolicy found = null; + for (RangerPolicy p : policies) { + if ("all - custom".equals(p.getName())) { + found = p; + break; + } + } + assertNotNull(found); + assertEquals(0, found.getPolicyItems() == null ? 0 : found.getPolicyItems().size()); + } + + @Test + public void test06_getDefaultRangerPolicies_withLookupUser() throws Exception { + Map configs = new HashMap(); + buildHbaseConnectionConfig(configs); + configs.put("setup.additional.default.policies", "true"); + configs.put("default-policy.1.name", "all - custom"); + configs.put("default-policy.1.resource.path", "/"); + + RangerServiceDef sd = new RangerServiceDef(sdName, "org.apache.ranger.services.hbase.RangerServiceHBase", + "TestService", "test servicedef description", null, null, null, null, null, null, null); + RangerService svc = new RangerService(sdName, serviceName, "unit test default policies", null, configs); + + RangerServiceHBase svcHBase = new RangerServiceHBase(); + svcHBase.init(sd, svc); + + Field lookupUserField = RangerBaseService.class.getDeclaredField("lookUpUser"); + lookupUserField.setAccessible(true); + lookupUserField.set(svcHBase, "lookup_user"); + + List policies = svcHBase.getDefaultRangerPolicies(); + assertNotNull(policies); + + RangerPolicy found = null; + for (RangerPolicy p : policies) { + if ("all - custom".equals(p.getName())) { + found = p; + break; + } + } + assertNotNull(found); + + List items = found.getPolicyItems(); + assertNotNull(items); + assertEquals(1, items.size()); + + RangerPolicyItem item = items.get(0); + assertNotNull(item.getUsers()); + assertEquals(1, item.getUsers().size()); + assertEquals("lookup_user", item.getUsers().get(0)); + assertEquals(Boolean.FALSE, item.getDelegateAdmin()); + + List accesses = item.getAccesses(); + assertNotNull(accesses); + assertEquals(2, accesses.size()); + + List types = new ArrayList(); + for (RangerPolicyItemAccess a : accesses) { + types.add(a.getType()); + } + // Expect read and create added by RangerServiceHBase + assertEquals(true, types.contains("read")); + assertEquals(true, types.contains("create")); + } + + public static void buildHbaseConnectionConfig(Map configs) { configs.put("username", "hbaseuser"); configs.put("password", "*******"); configs.put("hadoop.security.authentication", "simple"); @@ -114,17 +266,11 @@ public void buildHbaseConnectionConfig() { configs.put("isencrypted", "true"); } - public void buildLookupContext() { + public static void buildLookupContext(ResourceLookupContext lookupContext) { Map> resourceMap = new HashMap>(); resourceMap.put(null, null); lookupContext.setUserInput("iem"); lookupContext.setResourceName("table"); lookupContext.setResources(resourceMap); } - - @After - public void tearDown() { - sd = null; - svc = null; - } } diff --git a/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseClient.java b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseClient.java new file mode 100644 index 0000000000..4edf5f993b --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseClient.java @@ -0,0 +1,609 @@ +/* + * 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.hbase.client; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseConfiguration; +import org.apache.hadoop.hbase.MasterNotRunningException; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.ZooKeeperConnectionException; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; +import org.apache.hadoop.hbase.client.Connection; +import org.apache.hadoop.hbase.client.ConnectionFactory; +import org.apache.hadoop.hbase.client.HBaseAdmin; +import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.ranger.plugin.client.HadoopException; +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 javax.security.auth.Subject; + +import java.io.IOException; +import java.security.PrivilegedAction; +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.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHBaseClient { + @Test + public void test01_connectionTest_success() throws Exception { + Map configs = new HashMap<>(); + configs.put("username", "user"); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(HBaseClient.class, + (mock, context) -> { + Mockito.when(mock.getHBaseStatus()).thenReturn(true); + })) { + Map result = HBaseClient.connectionTest("svc", configs); + assertNotNull(result); + assertEquals(true, result.get("connectivityStatus")); + assertEquals("ConnectionTest Successful", result.get("message")); + } + } + + @Test + public void test02_connectionTest_failure() throws Exception { + Map configs = new HashMap<>(); + configs.put("username", "user"); + + try (MockedConstruction mockedConstruction = Mockito.mockConstruction(HBaseClient.class, + (mock, context) -> { + Mockito.when(mock.getHBaseStatus()).thenReturn(false); + })) { + Map result = HBaseClient.connectionTest("svc", configs); + assertNotNull(result); + assertEquals(false, result.get("connectivityStatus")); + assertTrue(((String) result.get("description")).contains("Unable to retrieve any databases")); + } + } + + @Test + public void test03_getHBaseStatus_success() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class); + MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))).thenAnswer(inv -> null); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + boolean status = client.getHBaseStatus(); + assertTrue(status); + } + } + + @Test + public void test04_getHBaseStatus_zkException() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class); + MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))) + .thenThrow(new ZooKeeperConnectionException("zk fail")); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + HadoopException ex = assertThrows(HadoopException.class, client::getHBaseStatus); + assertTrue(ex.getMessage().contains("Unable to connect to `ZooKeeper`")); + } + } + + @Test + public void test05_getHBaseStatus_masterNotRunning() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class); + MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))) + .thenThrow(new MasterNotRunningException("master down")); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + HadoopException ex = assertThrows(HadoopException.class, client::getHBaseStatus); + assertTrue(ex.getMessage().contains("Master")); + } + } + + @Test + public void test06_getHBaseStatus_ioException() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class); + MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))) + .thenThrow(new IOException("io")); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + HadoopException ex = assertThrows(HadoopException.class, client::getHBaseStatus); + assertTrue(ex.getMessage().contains("Unable to check availability of Hbase environment")); + } + } + + @Test + public void test07_getHBaseStatus_throwable() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class); + MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))) + .thenThrow(new RuntimeException("boom")); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + HadoopException ex = assertThrows(HadoopException.class, client::getHBaseStatus); + assertTrue(ex.getMessage().contains("Unable to check availability of Hbase environment")); + } + } + + @Test + public void test08_getTableList_filtersAndExceptions() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + + TableDescriptor td1 = Mockito.mock(TableDescriptor.class); + TableDescriptor td2 = Mockito.mock(TableDescriptor.class); + TableName tn1 = TableName.valueOf("t1"); + TableName tn2 = TableName.valueOf("t2"); + Mockito.when(td1.getTableName()).thenReturn(tn1); + Mockito.when(td2.getTableName()).thenReturn(tn2); + Mockito.when(admin.listTableDescriptors(Mockito.any(Pattern.class))).thenReturn(Arrays.asList(td1, td2)); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + List existing = new ArrayList<>(Collections.singletonList("t1")); + List ret = client.getTableList("t.*", existing); + assertEquals(Collections.singletonList("t2"), ret); + + Mockito.when(admin.listTableDescriptors(Mockito.any(Pattern.class))).thenThrow(new IOException("io")); + assertThrows(HadoopException.class, () -> client.getTableList("t.*", null)); + } + } + + @Test + public void test09_getColumnFamilyList_filtersAndExceptions() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + + TableDescriptor td = Mockito.mock(TableDescriptor.class); + TableName tn = TableName.valueOf("t1"); + Mockito.when(admin.getDescriptor(tn)).thenReturn(td); + ColumnFamilyDescriptor cfd1 = mockCfd("cf1"); + ColumnFamilyDescriptor cfd2 = mockCfd("cf2"); + Mockito.when(td.getColumnFamilies()).thenReturn(new ColumnFamilyDescriptor[] {cfd1, cfd2}); + + HBaseClient client = new TestableHBaseClient("svc", props, subject); + + List tables = new ArrayList<>(Collections.singletonList("t1")); + List existing = new ArrayList<>(Collections.singletonList("cf1")); + List ret = client.getColumnFamilyList("cf.*", tables, existing); + assertEquals(Collections.singletonList("cf2"), ret); + + Mockito.when(admin.getDescriptor(tn)).thenThrow(new IOException("io")); + assertThrows(HadoopException.class, () -> client.getColumnFamilyList("cf.*", tables, null)); + } + } + + @Test + public void test10_addDefaultHBaseProp_behavior() { + Map cfg = new HashMap<>(); + cfg.put("hadoop.security.authorization", "false"); + cfg.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + HBaseClient client = new TestableHBaseClient("svc", cfg, null); + Assertions.assertNotNull(client); + assertTrue(cfg.containsKey("zookeeper.znode.parent")); + assertEquals("/hbase-unsecure", cfg.get("zookeeper.znode.parent")); + } + } + + @Test + public void test10a_addDefaultHBaseProp_existingZnodeNoChange() { + Map cfg = new HashMap<>(); + cfg.put("hadoop.security.authorization", "false"); + cfg.put("zookeeper.znode.parent", "/custom"); + cfg.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + HBaseClient client = new TestableHBaseClient("svc", cfg, null); + Assertions.assertNotNull(client); + assertEquals("/custom", cfg.get("zookeeper.znode.parent")); + } + } + + @Test + public void test11_setClientConfigValues_updatesExistingKeysOnly() { + Map cfg = new HashMap<>(); + cfg.put("username", "user"); + cfg.put("x", "Y"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + Subject subject = Mockito.mock(Subject.class); + HBaseClient client = new TestableHBaseClient("svc", cfg, subject); + + // Clear initial conf.set calls from constructor + Mockito.clearInvocations(conf); + + Mockito.doReturn(null).when(conf).get(Mockito.anyString()); + Mockito.doReturn("different").when(conf).get("x"); + try (MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic adminStatic = Mockito.mockStatic(HBaseAdmin.class)) { + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction action = inv.getArgument(1); + return action.run(); + }); + + adminStatic.when(() -> HBaseAdmin.available(Mockito.any(Configuration.class))).thenAnswer(inv -> null); + + client.getHBaseStatus(); + Mockito.verify(conf).set("x", "Y"); + } + } + } + + @Test + public void test12_getTableList_zkException() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenThrow(new ZooKeeperConnectionException("zk")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + assertThrows(HadoopException.class, () -> client.getTableList("t.*", null)); + } + } + + @Test + public void test13_getTableList_masterNotRunning() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + Mockito.when(admin.listTableDescriptors(Mockito.any(Pattern.class))) + .thenThrow(new MasterNotRunningException("down")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + assertThrows(HadoopException.class, () -> client.getTableList("t.*", null)); + } + } + + @Test + public void test14_getTableList_throwable() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + Mockito.when(admin.listTableDescriptors(Mockito.any(Pattern.class))) + .thenThrow(new RuntimeException("boom")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + assertThrows(HadoopException.class, () -> client.getTableList("t.*", null)); + } + } + + @Test + public void test15_getColumnFamilyList_zkException() throws Exception { + Map props = new HashMap<>(); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenThrow(new ZooKeeperConnectionException("zk")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + List tables = new ArrayList<>(Collections.singletonList("t1")); + assertThrows(HadoopException.class, () -> client.getColumnFamilyList("cf.*", tables, null)); + } + } + + @Test + public void test16_getColumnFamilyList_masterNotRunning() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + + Mockito.when(admin.getDescriptor(Mockito.any(TableName.class))) + .thenThrow(new MasterNotRunningException("down")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + List tables = new ArrayList<>(Collections.singletonList("t1")); + assertThrows(HadoopException.class, () -> client.getColumnFamilyList("cf.*", tables, null)); + } + } + + @Test + public void test17_getColumnFamilyList_securityException() { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenThrow(new SecurityException("sec")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + List tables = new ArrayList<>(Collections.singletonList("t1")); + assertThrows(HadoopException.class, () -> client.getColumnFamilyList("cf.*", tables, null)); + } + } + + @Test + public void test18_getColumnFamilyList_throwable() throws Exception { + Map props = new HashMap<>(); + props.put("username", "user"); + + try (MockedStatic confStatic = Mockito.mockStatic(HBaseConfiguration.class); + MockedStatic subjectStatic = Mockito.mockStatic(Subject.class); + MockedStatic connFactoryStatic = Mockito.mockStatic(ConnectionFactory.class)) { + Configuration conf = Mockito.mock(Configuration.class); + confStatic.when(HBaseConfiguration::create).thenReturn(conf); + + subjectStatic.when(() -> Subject.doAs(Mockito.any(), Mockito.any(PrivilegedAction.class))) + .thenAnswer(inv -> { + PrivilegedAction a = inv.getArgument(1); + return a.run(); + }); + + Connection connection = Mockito.mock(Connection.class); + Admin admin = Mockito.mock(Admin.class); + connFactoryStatic.when(() -> ConnectionFactory.createConnection(Mockito.any(Configuration.class))) + .thenReturn(connection); + Mockito.when(connection.getAdmin()).thenReturn(admin); + + Mockito.when(admin.getDescriptor(Mockito.any(TableName.class))).thenThrow(new RuntimeException("boom")); + + HBaseClient client = new TestableHBaseClient("svc", props, Mockito.mock(Subject.class)); + List tables = new ArrayList<>(Collections.singletonList("t1")); + assertThrows(HadoopException.class, () -> client.getColumnFamilyList("cf.*", tables, null)); + } + } + + private ColumnFamilyDescriptor mockCfd(String name) { + ColumnFamilyDescriptor cfd = Mockito.mock(ColumnFamilyDescriptor.class); + Mockito.when(cfd.getNameAsString()).thenReturn(name); + return cfd; + } + + private static class TestableHBaseClient extends HBaseClient { + private final Subject testSubject; + + public TestableHBaseClient(String serviceName, Map connectionProp, Subject subject) { + super(serviceName, connectionProp); + this.testSubject = subject; + } + + @Override + protected void login() { + // no-op to avoid real environment login + } + + @Override + protected Subject getLoginSubject() { + return testSubject; + } + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseConnectionMgr.java b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseConnectionMgr.java new file mode 100644 index 0000000000..8de40461fa --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseConnectionMgr.java @@ -0,0 +1,120 @@ +/* + * 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.hbase.client; + +import org.apache.ranger.plugin.util.TimedEventUtil; +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.HashMap; +import java.util.Map; +import java.util.concurrent.Callable; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHBaseConnectionMgr { + @Test + public void test01_cacheMiss_thenPutIfAbsent_andCacheHit() throws Exception { + HBaseConnectionMgr mgr = new HBaseConnectionMgr(); + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + + try (MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class); + MockedConstruction construct = Mockito.mockConstruction(HBaseClient.class, (mock, ctx) -> {})) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable c = (Callable) inv.getArgument(0); + return c.call(); + }); + + HBaseClient c1 = mgr.getHBaseConnection("svc", "type", cfg); + assertNotNull(c1); + + HBaseClient cached = mgr.getHBaseConnection("svc", "type", cfg); + assertSame(c1, cached); + } + } + + @Test + public void test02_cacheHit_butInvalid_thenReconnect() throws Exception { + HBaseConnectionMgr mgr = new HBaseConnectionMgr(); + Map cfg = new HashMap<>(); + cfg.put("username", "u"); + + try (MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class); + MockedConstruction construct = Mockito.mockConstruction(HBaseClient.class, (mock, ctx) -> {})) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable c = (Callable) inv.getArgument(0); + return c.call(); + }); + + HBaseClient first = Mockito.mock(HBaseClient.class); + HBaseClient c1 = mgr.getHBaseConnection("svc2", "type", cfg); + mgr.hbaseConnectionCache.put("svc2", first); + + Mockito.when(first.getTableList(Mockito.anyString(), Mockito.isNull())).thenReturn(null); + + HBaseClient result = mgr.getHBaseConnection("svc2", "type", cfg); + assertNotNull(result); + } + } + + @Test + public void test03_nullConfigsPath_usesNullBranch() throws Exception { + HBaseConnectionMgr mgr = new HBaseConnectionMgr(); + + try (MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class); + MockedConstruction construct = Mockito.mockConstruction(HBaseClient.class, (mock, ctx) -> {})) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable c = (Callable) inv.getArgument(0); + return c.call(); + }); + + HBaseClient c1 = mgr.getHBaseConnection("svc3", "type", null); + assertNotNull(c1); + } + } + + @Test + public void test04_nullServiceType_returnsNullAndNoThrow() { + HBaseConnectionMgr mgr = new HBaseConnectionMgr(); + HBaseClient client = mgr.getHBaseConnection("svc", null, new HashMap<>()); + assertNull(client); + } +} diff --git a/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseResourceMgr.java b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseResourceMgr.java new file mode 100644 index 0000000000..5b628b9166 --- /dev/null +++ b/hbase-agent/src/test/java/org/apache/ranger/services/hbase/client/TestHBaseResourceMgr.java @@ -0,0 +1,216 @@ +/* + * 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.hbase.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.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.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +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; + +/** + * @generated by Cursor + * @description + */ +@ExtendWith(MockitoExtension.class) +@TestMethodOrder(MethodOrderer.MethodName.class) +public class TestHBaseResourceMgr { + @Test + public void test01_connectionTest_delegates() throws Exception { + Map cfg = new HashMap<>(); + try (MockedStatic clientStatic = Mockito.mockStatic(HBaseClient.class)) { + Map expected = new HashMap<>(); + expected.put("connectivityStatus", true); + clientStatic.when(() -> HBaseClient.connectionTest("svc", cfg)).thenReturn(expected); + + Map ret = HBaseResourceMgr.connectionTest("svc", cfg); + assertEquals(expected, ret); + } + } + + @Test + public void test01a_connectionTest_propagatesException() throws Exception { + Map cfg = new HashMap<>(); + try (MockedStatic clientStatic = Mockito.mockStatic(HBaseClient.class)) { + clientStatic.when(() -> HBaseClient.connectionTest("svc", cfg)).thenThrow(new HadoopException("boom")); + assertThrows(HadoopException.class, () -> HBaseResourceMgr.connectionTest("svc", cfg)); + } + } + + @Test + public void test02_getHBaseResource_tableFlow() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setResourceName("table"); + ctx.setUserInput("tab"); + + List existingTables = new ArrayList<>(); + Map> resources = new HashMap<>(); + resources.put("table", existingTables); + ctx.setResources(resources); + + try (MockedConstruction mgrConstr = Mockito.mockConstruction(HBaseConnectionMgr.class, + (mgrMock, c) -> { + HBaseClient client = Mockito.mock(HBaseClient.class); + Mockito.when(mgrMock.getHBaseConnection(Mockito.eq("svc"), Mockito.eq("type"), Mockito.any())) + .thenReturn(client); + Mockito.when(client.getTableList(Mockito.eq("tab.*".replaceAll("\\*", ".\\*")), + Mockito.eq(existingTables))) + .thenReturn(new ArrayList<>(Arrays.asList("tab1", "tab2"))); + }); MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class)) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable> c = (Callable>) inv + .getArgument(0); + return c.call(); + }); + + Map conf = new HashMap<>(); + conf.put("k", "v"); + List ret = HBaseResourceMgr.getHBaseResource("svc", "type", conf, ctx); + assertNotNull(ret); + assertEquals(2, ret.size()); + } + } + + @Test + public void test03_getHBaseResource_columnFamilyFlow() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setResourceName("column-family"); + ctx.setUserInput("cf"); + + List existingTables = new ArrayList<>(); + existingTables.add("t1"); + Map> resources = new HashMap<>(); + resources.put("table", existingTables); + ctx.setResources(resources); + + try (MockedConstruction mgrConstr = Mockito.mockConstruction(HBaseConnectionMgr.class, + (mgrMock, c) -> { + HBaseClient client = Mockito.mock(HBaseClient.class); + Mockito.when(mgrMock.getHBaseConnection(Mockito.eq("svc"), Mockito.eq("type"), Mockito.any())) + .thenReturn(client); + Mockito.when(client.getColumnFamilyList(Mockito.eq("cf.*".replaceAll("\\*", ".\\*")), + Mockito.eq(existingTables), Mockito.isNull())) + .thenReturn(new ArrayList<>(Arrays.asList("cf1", "cf2"))); + }); MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class)) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable> c = (Callable>) inv + .getArgument(0); + return c.call(); + }); + + Map conf = new HashMap<>(); + conf.put("k", "v"); + List ret = HBaseResourceMgr.getHBaseResource("svc", "type", conf, ctx); + assertNotNull(ret); + assertEquals(2, ret.size()); + } + } + + @Test + public void test04_getHBaseResource_propagatesException() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setResourceName("table"); + ctx.setUserInput("tab"); + + try (MockedConstruction mgrConstr = Mockito.mockConstruction(HBaseConnectionMgr.class, + (mgrMock, c) -> { + Mockito.when(mgrMock.getHBaseConnection(Mockito.eq("svc"), Mockito.eq("type"), Mockito.any())) + .thenThrow(new HadoopException("boom")); + }); MockedStatic timed = Mockito.mockStatic(TimedEventUtil.class)) { + timed.when(() -> TimedEventUtil.timedTask(Mockito.any(), Mockito.anyLong(), Mockito.any())) + .thenAnswer(inv -> { + @SuppressWarnings("unchecked") + Callable> callable = (Callable>) inv.getArgument(0); + return callable.call(); + }); + + Map conf = new HashMap<>(); + conf.put("k", "v"); + assertThrows(HadoopException.class, () -> HBaseResourceMgr.getHBaseResource("svc", "type", conf, ctx)); + } + } + + @Test + public void test05_getHBaseResource_nullInputs_returnNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + // userInput and resourceName remain null + + List ret = HBaseResourceMgr.getHBaseResource("svc", "type", new HashMap<>(), ctx); + assertNull(ret); + } + + @Test + public void test06_getHBaseResource_unknownResource_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setResourceName("unknown"); + ctx.setUserInput("abc"); + + try (MockedConstruction mgrConstr = Mockito.mockConstruction(HBaseConnectionMgr.class, + (mgrMock, c) -> { + Mockito.when(mgrMock.getHBaseConnection(Mockito.eq("svc"), Mockito.eq("type"), Mockito.any())) + .thenReturn(Mockito.mock(HBaseClient.class)); + })) { + List ret = HBaseResourceMgr.getHBaseResource("svc", "type", new HashMap<>(), ctx); + assertNull(ret); + } + } + + @Test + public void test07_getHBaseResource_emptyColumnFamilyInput_returnsNull() throws Exception { + ResourceLookupContext ctx = new ResourceLookupContext(); + ctx.setResourceName("column-family"); + ctx.setUserInput(""); + + Map> resources = new HashMap<>(); + resources.put("table", new ArrayList<>()); + ctx.setResources(resources); + + try (MockedConstruction mgrConstr = Mockito.mockConstruction(HBaseConnectionMgr.class, + (mgrMock, c) -> { + Mockito.when(mgrMock.getHBaseConnection(Mockito.eq("svc"), Mockito.eq("type"), Mockito.any())) + .thenReturn(Mockito.mock(HBaseClient.class)); + })) { + List ret = HBaseResourceMgr.getHBaseResource("svc", "type", new HashMap<>(), ctx); + assertNull(ret); + } + } +}