This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/storm.git


The following commit(s) were added to refs/heads/master by this push:
     new 0441cfc2b Apply the nimbus ACLs to listBlobs so callers only see blobs 
they may read (#9000)
0441cfc2b is described below

commit 0441cfc2b9d521029459c85ba8cd5ff77f2fa8cc
Author: Richard Zowalla <[email protected]>
AuthorDate: Sun Aug 23 21:15:40 2026 +0200

    Apply the nimbus ACLs to listBlobs so callers only see blobs they may read 
(#9000)
---
 .../auth/authorizer/SimpleACLAuthorizer.java       |  1 +
 .../auth/authorizer/SimpleACLAuthorizerTest.java   |  5 +++++
 .../org/apache/storm/daemon/nimbus/Nimbus.java     | 13 +++++++++--
 .../org/apache/storm/daemon/nimbus/NimbusTest.java | 26 ++++++++++++++++++++++
 4 files changed, 43 insertions(+), 2 deletions(-)

diff --git 
a/storm-client/src/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizer.java
 
b/storm-client/src/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizer.java
index 39dcbe42d..399a9b049 100644
--- 
a/storm-client/src/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizer.java
+++ 
b/storm-client/src/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizer.java
@@ -40,6 +40,7 @@ public class SimpleACLAuthorizer implements IAuthorizer {
         "submitTopology",
         "fileUpload",
         "getNimbusConf",
+        "listBlobs",
         "getClusterInfo",
         "getLeader",
         "isTopologyNameAllowed",
diff --git 
a/storm-client/test/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizerTest.java
 
b/storm-client/test/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizerTest.java
index e151f4826..63eaf7922 100644
--- 
a/storm-client/test/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizerTest.java
+++ 
b/storm-client/test/jvm/org/apache/storm/security/auth/authorizer/SimpleACLAuthorizerTest.java
@@ -68,6 +68,11 @@ public class SimpleACLAuthorizerTest {
         assertTrue(authorizer.permit(new ReqContext(userA), "getNimbusConf", 
new HashMap<>()));
         assertTrue(authorizer.permit(new ReqContext(userB), "getNimbusConf", 
new HashMap<>()));
 
+        assertTrue(authorizer.permit(new ReqContext(adminUser), "listBlobs", 
new HashMap<>()));
+        assertFalse(authorizer.permit(new ReqContext(supervisorUser), 
"listBlobs", new HashMap<>()));
+        assertTrue(authorizer.permit(new ReqContext(userA), "listBlobs", new 
HashMap<>()));
+        assertTrue(authorizer.permit(new ReqContext(userB), "listBlobs", new 
HashMap<>()));
+
         assertTrue(authorizer.permit(new ReqContext(adminUser), 
"getClusterInfo", new HashMap<>()));
         assertFalse(authorizer.permit(new ReqContext(supervisorUser), 
"getClusterInfo", new HashMap<>()));
         assertTrue(authorizer.permit(new ReqContext(userA), "getClusterInfo", 
new HashMap<>()));
diff --git 
a/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java 
b/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
index 2d7704aaa..ab0614424 100644
--- a/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
+++ b/storm-server/src/main/java/org/apache/storm/daemon/nimbus/Nimbus.java
@@ -4070,6 +4070,7 @@ public class Nimbus implements Iface, Shutdownable, 
DaemonCommon {
     @Override
     public ListBlobsResult listBlobs(String session) throws TException {
         try {
+            checkAuthorization(null, null, "listBlobs");
             Iterator<String> keyIt;
             //Create a new session id if the user gave an empty session string.
             // This is the use case when the user wishes to list blobs
@@ -4092,9 +4093,17 @@ public class Nimbus implements Iface, Shutdownable, 
DaemonCommon {
                 return new ListBlobsResult(Collections.emptyList(), session);
             }
 
+            Subject who = getSubject();
             ArrayList<String> listChunk = new ArrayList<>();
-            for (int i = 0; i < 100 && keyIt.hasNext(); i++) {
-                listChunk.add(keyIt.next());
+            while (listChunk.size() < 100 && keyIt.hasNext()) {
+                String key = keyIt.next();
+                //Only list the blobs whose metadata the caller may read, the 
same check getBlobMeta does.
+                try {
+                    blobStore.getBlobMeta(key, who);
+                    listChunk.add(key);
+                } catch (AuthorizationException | KeyNotFoundException e) {
+                    LOG.debug("Not listing blob {} for {}", key, who);
+                }
             }
             blobListers.put(session, keyIt);
             LOG.info("Downloading {} entries", listChunk.size());
diff --git 
a/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java 
b/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
index 96336f7cc..2326a94eb 100644
--- a/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
+++ b/storm-server/src/test/java/org/apache/storm/daemon/nimbus/NimbusTest.java
@@ -23,6 +23,7 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
@@ -39,6 +40,7 @@ import org.apache.storm.cluster.IStormClusterState;
 import org.apache.storm.generated.AuthorizationException;
 import org.apache.storm.generated.InvalidTopologyException;
 import org.apache.storm.generated.KeyNotFoundException;
+import org.apache.storm.generated.ListBlobsResult;
 import org.apache.storm.generated.RebalanceOptions;
 import org.apache.storm.generated.StormTopology;
 import org.apache.storm.metric.StormMetricsRegistry;
@@ -52,11 +54,13 @@ import 
org.apache.storm.scheduler.resource.strategies.scheduling.RoundRobinResou
 import org.apache.storm.security.auth.IGroupMappingServiceProvider;
 import org.apache.storm.security.auth.ReqContext;
 import org.apache.storm.security.auth.SingleUserPrincipal;
+import org.apache.storm.security.auth.authorizer.DenyAuthorizer;
 import org.apache.storm.testing.TestWordSpout;
 import org.apache.storm.thrift.TException;
 import org.apache.storm.topology.TopologyBuilder;
 import org.apache.storm.utils.ServerUtils;
 import org.apache.storm.utils.Time;
+import org.apache.storm.utils.WrappedAuthorizationException;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentCaptor;
@@ -214,6 +218,28 @@ class NimbusTest {
         }
     }
 
+    @Test
+    void testListBlobsOnlyReturnsKeysTheCallerMayReadTheMetadataOf() throws 
Exception {
+        when(localBlobStore.listKeys()).thenReturn(List.of("readable-key", 
"other-users-key").iterator());
+        when(localBlobStore.getBlobMeta(eq("other-users-key"), any()))
+            .thenThrow(new WrappedAuthorizationException("not allowed"));
+
+        ListBlobsResult result = nimbus.listBlobs("");
+
+        assertEquals(List.of("readable-key"), result.get_keys());
+    }
+
+    @Test
+    void testListBlobsIsAuthorized() throws Exception {
+        Map<String, Object> conf = 
Map.of(DaemonConfig.NIMBUS_MONITOR_FREQ_SECS, 10,
+                                          DaemonConfig.NIMBUS_AUTHORIZER, 
DenyAuthorizer.class.getName());
+        nimbus = new Nimbus(conf, iNimbus, stormClusterState, nimbusInfo, 
localBlobStore, leaderElector, groupMapper, metricRegistry);
+        
when(localBlobStore.listKeys()).thenReturn(List.of("readable-key").iterator());
+
+        assertThrows(AuthorizationException.class, () -> nimbus.listBlobs(""));
+        verify(localBlobStore, never()).listKeys();
+    }
+
     @Test
     void testValidateUploadedJarLocationRejectsLocationsOutsideTheInbox() 
throws Exception {
         Path inbox = Files.createTempDirectory("nimbus-inbox");

Reply via email to