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");