From a7791c8828ebf755ffc117c3a78110b78ee4caca Mon Sep 17 00:00:00 2001 From: shml Date: Tue, 25 Aug 2026 18:22:55 +0800 Subject: [PATCH] RANGER-5763: Fix NPE in HBase plugin snapshot hooks when table descriptor is null --- .../hbase/RangerAuthorizationCoprocessor.java | 15 ++++++++++ .../RangerAuthorizationCoprocessorTest.java | 29 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java index ffd642613a..faf11c45bc 100644 --- a/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java +++ b/hbase-agent/src/main/java/org/apache/ranger/authorization/hbase/RangerAuthorizationCoprocessor.java @@ -319,16 +319,31 @@ public void postStartMaster(ObserverContext ctx) t @Override public void preSnapshot(ObserverContext ctx, SnapshotDescription snapshot, TableDescriptor hTableDescriptor) throws IOException { + if (hTableDescriptor == null) { + // HBase can call this hook with a null table descriptor when the table does not exist (HBASE-29361); the operation fails in HBase anyway + return; + } + requirePermission(ctx, "snapshot", hTableDescriptor.getTableName().getName(), Permission.Action.ADMIN); } @Override public void preCloneSnapshot(ObserverContext ctx, SnapshotDescription snapshot, TableDescriptor hTableDescriptor) throws IOException { + if (hTableDescriptor == null) { + // HBase can call this hook with a null table descriptor when the table does not exist (HBASE-29361); the operation fails in HBase anyway + return; + } + requirePermission(ctx, "cloneSnapshot", hTableDescriptor.getTableName().getName(), Permission.Action.ADMIN); } @Override public void preRestoreSnapshot(ObserverContext ctx, SnapshotDescription snapshot, TableDescriptor hTableDescriptor) throws IOException { + if (hTableDescriptor == null) { + // HBase can call this hook with a null table descriptor when the table does not exist (HBASE-29361); the operation fails in HBase anyway + return; + } + requirePermission(ctx, "restoreSnapshot", hTableDescriptor.getTableName().getName(), Permission.Action.ADMIN); } 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 340ac18492..3776937cb3 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 @@ -45,7 +45,9 @@ 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.SnapshotType; import org.apache.hadoop.hbase.client.TableDescriptor; +import org.apache.hadoop.hbase.client.TableDescriptorBuilder; import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; import org.apache.hadoop.hbase.coprocessor.ObserverContext; import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; @@ -127,6 +129,7 @@ import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -2878,4 +2881,30 @@ public void test115_requirePermission_collection_accessDenied() throws Exception // cleanup pf.set(null, null); } + + @Test + public void test116_preSnapshotHooks_skipPermissionCheckWhenTableDescriptorNull() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + ObserverContext ctx = mock(ObserverContext.class); + SnapshotDescription snapshot = new SnapshotDescription("snap", TableName.valueOf("nonexistent"), SnapshotType.FLUSH); + + cp.preSnapshot(ctx, snapshot, null); + cp.preCloneSnapshot(ctx, snapshot, null); + cp.preRestoreSnapshot(ctx, snapshot, null); + + verify(cp, never()).requirePermission(any(ObserverContext.class), anyString(), any(byte[].class), any(Permission.Action.class)); + } + + @Test + public void test117_preSnapshotHooks_checkPermissionWhenTableDescriptorPresent() throws Exception { + RangerAuthorizationCoprocessor cp = spy(new RangerAuthorizationCoprocessor()); + doNothing().when(cp).requirePermission(any(ObserverContext.class), anyString(), any(byte[].class), any(Permission.Action.class)); + ObserverContext ctx = mock(ObserverContext.class); + SnapshotDescription snapshot = new SnapshotDescription("snap", TableName.valueOf("t"), SnapshotType.FLUSH); + TableDescriptor tableDescriptor = TableDescriptorBuilder.newBuilder(TableName.valueOf("t")).build(); + + cp.preSnapshot(ctx, snapshot, tableDescriptor); + + verify(cp).requirePermission(any(ObserverContext.class), eq("snapshot"), any(byte[].class), any(Permission.Action.class)); + } }