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 b7865e47a Apply the configured log user and group lists to daemon log 
requests (#8973)
b7865e47a is described below

commit b7865e47abadb315556a694dcdbb7a8568bae3ee
Author: Richard Zowalla <[email protected]>
AuthorDate: Sat Aug 22 19:29:55 2026 +0200

    Apply the configured log user and group lists to daemon log requests (#8973)
---
 .../logviewer/handler/LogviewerLogPageHandler.java |   4 +
 .../handler/LogviewerLogSearchHandler.java         |   3 +-
 .../daemon/logviewer/utils/LogFileDownloader.java  |   3 +-
 .../daemon/logviewer/utils/ResourceAuthorizer.java |  35 +++++++
 .../handler/LogviewerLogDownloadHandlerTest.java   |  56 +++++++++-
 .../handler/LogviewerLogPageHandlerTest.java       |  43 +++++++-
 .../handler/LogviewerLogSearchHandlerTest.java     |  51 +++++++++
 .../logviewer/utils/ResourceAuthorizerTest.java    | 114 +++++++++++++++++++++
 8 files changed, 305 insertions(+), 4 deletions(-)

diff --git 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java
 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java
index 5917e9746..b1144ca65 100644
--- 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java
+++ 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandler.java
@@ -313,6 +313,10 @@ public class LogviewerLogPageHandler {
             return LogviewerResponseBuilder.buildResponsePageNotFound();
         }
 
+        if (!resourceAuthorizer.isUserAllowedToAccessDaemonFile(user)) {
+            return 
LogviewerResponseBuilder.buildResponseUnauthorizedUser(user);
+        }
+
         if (file.toFile().exists()) {
             // all types of files included
             List<File> logFiles = 
Arrays.stream(daemonLogRoot.toFile().listFiles())
diff --git 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java
 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java
index 3c884b08e..99da13fb1 100644
--- 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java
+++ 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandler.java
@@ -165,7 +165,8 @@ public class LogviewerLogSearchHandler {
         }
         Response response;
         if (absFile.toFile().exists()) {
-            if (isDaemon || resourceAuthorizer.isUserAllowedToAccessFile(user, 
fileName)) {
+            if (isDaemon ? 
resourceAuthorizer.isUserAllowedToAccessDaemonFile(user)
+                    : resourceAuthorizer.isUserAllowedToAccessFile(user, 
fileName)) {
                 Integer numMatchesInt = numMatchesStr != null ? 
tryParseIntParam("num-matches", numMatchesStr) : null;
                 Integer offsetInt = offsetStr != null ? 
tryParseIntParam("start-byte-offset", offsetStr) : null;
 
diff --git 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java
 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java
index 919c0df04..04e08bfc4 100644
--- 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java
+++ 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java
@@ -81,7 +81,8 @@ public class LogFileDownloader {
         }
         
         if (file.toFile().exists()) {
-            if (isDaemon || resourceAuthorizer.isUserAllowedToAccessFile(user, 
fileName)) {
+            if (isDaemon ? 
resourceAuthorizer.isUserAllowedToAccessDaemonFile(user)
+                    : resourceAuthorizer.isUserAllowedToAccessFile(user, 
fileName)) {
                 if (!isDaemon) {
                     //Only widen the permission of a worker log once the 
request is known to be served
                     workerLogs.setLogFilePermission(fileName);
diff --git 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java
 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java
index e88431cc9..9f321fc19 100644
--- 
a/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java
+++ 
b/storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizer.java
@@ -72,6 +72,41 @@ public class ResourceAuthorizer {
         return !isLogviewerFilterConfigured() || isAuthorizedLogUser(user, 
fileName);
     }
 
+    /**
+     * Checks whether user is allowed to access a daemon log file via UI. 
Daemon logs have no owning topology, so only the
+     * cluster level lists are consulted. Always true when the Logviewer 
filter is not configured.
+     *
+     * @param user username
+     */
+    public boolean isUserAllowedToAccessDaemonFile(String user) {
+        return !isLogviewerFilterConfigured() || 
isAuthorizedDaemonLogUser(user);
+    }
+
+    /**
+     * Checks whether user is authorized to access daemon log files. Checks 
regardless of UI filter.
+     *
+     * @param user username
+     */
+    public boolean isAuthorizedDaemonLogUser(String user) {
+        if (StringUtils.isEmpty(user)) {
+            return false;
+        }
+
+        List<String> logsUsers = new ArrayList<>();
+        
logsUsers.addAll(ObjectReader.getStrings(stormConf.get(DaemonConfig.LOGS_USERS)));
+        
logsUsers.addAll(ObjectReader.getStrings(stormConf.get(Config.NIMBUS_ADMINS)));
+
+        List<String> logsGroups = new ArrayList<>();
+        
logsGroups.addAll(ObjectReader.getStrings(stormConf.get(DaemonConfig.LOGS_GROUPS)));
+        
logsGroups.addAll(ObjectReader.getStrings(stormConf.get(Config.NIMBUS_ADMINS_GROUPS)));
+
+        String userName = principalToLocal.toLocal(user);
+        Set<String> groups = getUserGroups(userName);
+
+        return logsUsers.stream().anyMatch(u -> u.equals(userName))
+            || Sets.intersection(groups, new HashSet<>(logsGroups)).size() > 0;
+    }
+
     /**
      * Checks whether user is authorized to access file. Checks regardless of 
UI filter.
      *
diff --git 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandlerTest.java
 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandlerTest.java
index 5a6f34fda..0d40fce8d 100644
--- 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandlerTest.java
+++ 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandlerTest.java
@@ -31,11 +31,14 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import com.google.common.net.HttpHeaders;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Map;
 import jakarta.ws.rs.core.Response;
+import jakarta.ws.rs.core.StreamingOutput;
 import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer;
 import org.apache.storm.daemon.logviewer.utils.WorkerLogs;
 import org.apache.storm.metric.StormMetricsRegistry;
@@ -199,7 +202,58 @@ public class LogviewerLogDownloadHandlerTest {
         }
     }
 
+    @Test
+    public void testDownloadDaemonLogFileUnauthorizedUser() throws IOException 
{
+        try (TmpPath rootPath = new TmpPath()) {
+
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(false);
+            LogviewerLogDownloadHandler handler = 
createHandlerTraversalTests(rootPath.getFile().toPath(), resourceAuthorizer);
+
+            Response response = handler.downloadDaemonLogFile("host", 
"nimbus.log", "user");
+
+            Utils.forceDelete(rootPath.toString());
+
+            assertThat(response.getStatus(), 
is(Response.Status.FORBIDDEN.getStatusCode()));
+        }
+    }
+
+    @Test
+    public void testDownloadDaemonLogFileAuthorizedUser() throws IOException {
+        try (TmpPath rootPath = new TmpPath()) {
+
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(true);
+            LogviewerLogDownloadHandler handler = 
createHandlerTraversalTests(rootPath.getFile().toPath(), resourceAuthorizer);
+            //Give the daemon log some content, so that the response is only 
empty if the file was not served.
+            
Files.writeString(rootPath.getFile().toPath().resolve("logs").resolve("nimbus.log"),
 "nimbus log content");
+
+            Response response = handler.downloadDaemonLogFile("host", 
"nimbus.log", "user");
+            int status = response.getStatus();
+            String content = status == Response.Status.OK.getStatusCode() ? 
readEntity(response) : null;
+
+            Utils.forceDelete(rootPath.toString());
+
+            assertThat(status, is(Response.Status.OK.getStatusCode()));
+            assertThat(content, is("nimbus log content"));
+            String contentDisposition = 
response.getHeaderString(HttpHeaders.CONTENT_DISPOSITION);
+            assertThat(contentDisposition, containsString("host-nimbus.log"));
+            verify(resourceAuthorizer).isUserAllowedToAccessDaemonFile("user");
+        }
+    }
+
+    private String readEntity(Response response) throws IOException {
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ((StreamingOutput) response.getEntity()).write(out);
+        return out.toString(StandardCharsets.UTF_8);
+    }
+
     private LogviewerLogDownloadHandler createHandlerTraversalTests(Path 
rootPath) throws IOException {
+        return createHandlerTraversalTests(rootPath, new 
ResourceAuthorizer(Utils.readStormConfig()));
+    }
+
+    private LogviewerLogDownloadHandler createHandlerTraversalTests(Path 
rootPath, ResourceAuthorizer resourceAuthorizer)
+            throws IOException {
         Path daemonLogRoot = rootPath.resolve("logs");
         Path fileOutsideDaemonRoot = rootPath.resolve("evil.sh");
         Path workerLogRoot = daemonLogRoot.resolve("workers-artifacts");
@@ -221,7 +275,7 @@ public class LogviewerLogDownloadHandlerTest {
         Map<String, Object> stormConf = Utils.readStormConfig();
         StormMetricsRegistry metricsRegistry = new StormMetricsRegistry();
         return new LogviewerLogDownloadHandler(workerLogRoot.toString(), 
daemonLogRoot.toString(),
-            new WorkerLogs(stormConf, workerLogRoot, metricsRegistry), new 
ResourceAuthorizer(stormConf), metricsRegistry);
+            new WorkerLogs(stormConf, workerLogRoot, metricsRegistry), 
resourceAuthorizer, metricsRegistry);
     }
 
 }
diff --git 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandlerTest.java
 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandlerTest.java
index b6de62009..0d277cf8a 100644
--- 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandlerTest.java
+++ 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogPageHandlerTest.java
@@ -18,6 +18,7 @@
 
 package org.apache.storm.daemon.logviewer.handler;
 
+import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -214,7 +215,47 @@ public class LogviewerLogPageHandlerTest {
         }
     }
 
+    @Test
+    public void testDaemonLogPageUnauthorizedUser() throws Exception {
+        try (TmpPath rootPath = new TmpPath()) {
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(false);
+            LogviewerLogPageHandler handler = 
createHandlerForTraversalTests(rootPath.getFile().toPath(), resourceAuthorizer);
+            //Give the daemon log some content, so that an unauthorized 
request is the only reason not to render the page.
+            
Files.writeString(rootPath.getFile().toPath().resolve("logs").resolve("nimbus.log"),
 "nimbus log content");
+
+            final Response returned = handler.daemonLogPage("nimbus.log", 0, 
100, null, "user");
+
+            Utils.forceDelete(rootPath.toString());
+
+            assertThat(returned.getStatus(), 
is(Response.Status.FORBIDDEN.getStatusCode()));
+        }
+    }
+
+    @Test
+    public void testDaemonLogPageAuthorizedUser() throws Exception {
+        try (TmpPath rootPath = new TmpPath()) {
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(true);
+            LogviewerLogPageHandler handler = 
createHandlerForTraversalTests(rootPath.getFile().toPath(), resourceAuthorizer);
+            
Files.writeString(rootPath.getFile().toPath().resolve("logs").resolve("nimbus.log"),
 "nimbus log content");
+
+            final Response returned = handler.daemonLogPage("nimbus.log", 0, 
100, null, "user");
+
+            Utils.forceDelete(rootPath.toString());
+
+            assertThat(returned.getStatus(), 
is(Response.Status.OK.getStatusCode()));
+            assertThat((String) returned.getEntity(), containsString("nimbus 
log content"));
+            verify(resourceAuthorizer).isUserAllowedToAccessDaemonFile("user");
+        }
+    }
+
     private LogviewerLogPageHandler createHandlerForTraversalTests(Path 
rootPath) throws IOException {
+        return createHandlerForTraversalTests(rootPath, new 
ResourceAuthorizer(Utils.readStormConfig()));
+    }
+
+    private LogviewerLogPageHandler createHandlerForTraversalTests(Path 
rootPath, ResourceAuthorizer resourceAuthorizer)
+            throws IOException {
         Path daemonLogRoot = rootPath.resolve("logs");
         Path fileOutsideDaemonRoot = rootPath.resolve("evil.sh");
         Path daemonFile = daemonLogRoot.resolve("nimbus.log");
@@ -236,6 +277,6 @@ public class LogviewerLogPageHandlerTest {
         Map<String, Object> stormConf = Utils.readStormConfig();
         StormMetricsRegistry metricsRegistry = new StormMetricsRegistry();
         return new LogviewerLogPageHandler(workerLogRoot.toString(), 
daemonLogRoot.toString(),
-            new WorkerLogs(stormConf, workerLogRoot, metricsRegistry), new 
ResourceAuthorizer(stormConf), metricsRegistry);
+            new WorkerLogs(stormConf, workerLogRoot, metricsRegistry), 
resourceAuthorizer, metricsRegistry);
     }
 }
diff --git 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandlerTest.java
 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandlerTest.java
index 66415b3cf..90c15c861 100644
--- 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandlerTest.java
+++ 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogSearchHandlerTest.java
@@ -34,6 +34,8 @@ import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.internal.verification.VerificationModeFactory.times;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+
 import java.io.File;
 import java.io.IOException;
 import java.net.UnknownHostException;
@@ -47,12 +49,15 @@ import java.util.List;
 import java.util.Map;
 import java.util.function.Function;
 
+import jakarta.ws.rs.core.Response;
+
 import org.apache.storm.DaemonConfig;
 import org.apache.storm.daemon.logviewer.LogviewerConstant;
 import org.apache.storm.daemon.logviewer.utils.ResourceAuthorizer;
 import org.apache.storm.daemon.ui.InvalidRequestException;
 import org.apache.storm.metric.StormMetricsRegistry;
 import org.apache.storm.streams.tuple.Tuple3;
+import org.apache.storm.testing.TmpPath;
 import org.apache.storm.utils.Utils;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -863,6 +868,52 @@ public class LogviewerLogSearchHandlerTest {
         }
     }
 
+    @Test
+    public void testSearchDaemonLogFileUnauthorizedUser() throws Exception {
+        try (TmpPath rootPath = new TmpPath()) {
+            Path daemonLogRoot = rootPath.getFile().toPath().resolve("logs");
+            Files.createDirectories(daemonLogRoot);
+            Files.createFile(daemonLogRoot.resolve("nimbus.log"));
+
+            Map<String, Object> stormConf = Utils.readStormConfig();
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(false);
+            LogviewerLogSearchHandler handler = new 
LogviewerLogSearchHandler(stormConf, Paths.get(""), daemonLogRoot,
+                resourceAuthorizer, new StormMetricsRegistry());
+
+            Response response = handler.searchLogFile("nimbus.log", "user", 
true, "needle", null, null, null, null);
+
+            assertEquals(403, response.getStatus());
+        }
+    }
+
+    @Test
+    public void testSearchDaemonLogFileAuthorizedUser() throws Exception {
+        try (TmpPath rootPath = new TmpPath()) {
+            Path daemonLogRoot = rootPath.getFile().toPath().resolve("logs");
+            Files.createDirectories(daemonLogRoot);
+            Files.writeString(daemonLogRoot.resolve("nimbus.log"), "a needle 
in the daemon log\n");
+
+            Map<String, Object> stormConf = Utils.readStormConfig();
+            ResourceAuthorizer resourceAuthorizer = 
mock(ResourceAuthorizer.class);
+            
when(resourceAuthorizer.isUserAllowedToAccessDaemonFile(anyString())).thenReturn(true);
+            LogviewerLogSearchHandler handler = new 
LogviewerLogSearchHandler(stormConf, Paths.get(""), daemonLogRoot,
+                resourceAuthorizer, new StormMetricsRegistry());
+
+            Response response = handler.searchLogFile("nimbus.log", "user", 
true, "needle", null, null, null, null);
+
+            assertEquals(200, response.getStatus());
+            Map<?, ?> entity = new ObjectMapper().readValue((String) 
response.getEntity(), Map.class);
+            assertEquals("needle", entity.get("searchString"));
+            assertEquals("yes", entity.get("isDaemon"));
+            //A match must actually be reported, an empty match list would 
mean the file was never read.
+            List<?> matches = (List<?>) entity.get("matches");
+            assertEquals(1, matches.size());
+            assertEquals("needle", ((Map<?, ?>) 
matches.get(0)).get("matchString"));
+            verify(resourceAuthorizer).isUserAllowedToAccessDaemonFile("user");
+        }
+    }
+
     private static LogviewerLogSearchHandler getSearchHandler() {
         Map<String, Object> stormConf = Utils.readStormConfig();
         return new LogviewerLogSearchHandler(stormConf, Paths.get(""), 
Paths.get(""),
diff --git 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizerTest.java
 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizerTest.java
index 6db742432..96407bca1 100644
--- 
a/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizerTest.java
+++ 
b/storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/utils/ResourceAuthorizerTest.java
@@ -19,8 +19,10 @@
 package org.apache.storm.daemon.logviewer.utils;
 
 import static org.apache.storm.Config.NIMBUS_ADMINS;
+import static org.apache.storm.Config.NIMBUS_ADMINS_GROUPS;
 import static org.apache.storm.Config.TOPOLOGY_GROUPS;
 import static org.apache.storm.Config.TOPOLOGY_USERS;
+import static org.apache.storm.DaemonConfig.LOGS_GROUPS;
 import static org.apache.storm.DaemonConfig.LOGS_USERS;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -211,4 +213,116 @@ public class ResourceAuthorizerTest {
         authorized = authorizer.isUserAllowedToAccessFile("bob", "anyfile");
         assertFalse(authorized); // filter configured, should fail all users
     }
+
+    /**
+     * daemon logs are allowed for cluster logs users and cluster admins only.
+     */
+    @Test
+    public void 
testAuthorizedDaemonLogUserAllowsClusterLogsUserAndClusterAdmin() {
+        Map<String, Object> stormConf = Utils.readStormConfig();
+
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(LOGS_USERS, Collections.singletonList("alice"));
+        conf.put(NIMBUS_ADMINS, Collections.singletonList("bob"));
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.emptySet()).when(authorizer).getUserGroups(anyString());
+
+        assertTrue(authorizer.isAuthorizedDaemonLogUser("alice"));
+        assertTrue(authorizer.isAuthorizedDaemonLogUser("bob"));
+        assertFalse(authorizer.isAuthorizedDaemonLogUser("mallory"));
+    }
+
+    /**
+     * daemon logs are allowed for a user whose groups are listed in 
logs.groups.
+     */
+    @Test
+    public void testAuthorizedDaemonLogUserAllowsClusterLogsGroup() {
+        Map<String, Object> stormConf = Utils.readStormConfig();
+
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(LOGS_GROUPS, Collections.singletonList("alice-group"));
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.singleton("alice-group")).when(authorizer).getUserGroups(anyString());
+
+        //alice is not named in logs.users nor in nimbus.admins, she is 
authorized purely by her group membership
+        assertTrue(authorizer.isAuthorizedDaemonLogUser("alice"));
+    }
+
+    /**
+     * daemon logs are allowed for a user whose groups are listed in 
nimbus.admins.groups.
+     */
+    @Test
+    public void testAuthorizedDaemonLogUserAllowsClusterAdminGroup() {
+        Map<String, Object> stormConf = Utils.readStormConfig();
+
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(NIMBUS_ADMINS_GROUPS, 
Collections.singletonList("admin-group"));
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.singleton("admin-group")).when(authorizer).getUserGroups(anyString());
+
+        //alice is not named in logs.users nor in nimbus.admins, she is 
authorized purely by her group membership
+        assertTrue(authorizer.isAuthorizedDaemonLogUser("alice"));
+    }
+
+    /**
+     * daemon logs are denied for a user whose groups match neither 
logs.groups nor nimbus.admins.groups.
+     */
+    @Test
+    public void testAuthorizedDaemonLogUserDisallowsUnrelatedGroup() {
+        Map<String, Object> stormConf = Utils.readStormConfig();
+
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(LOGS_GROUPS, Collections.singletonList("logs-group"));
+        conf.put(NIMBUS_ADMINS_GROUPS, 
Collections.singletonList("admin-group"));
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.singleton("mallory-group")).when(authorizer).getUserGroups(anyString());
+
+        assertFalse(authorizer.isAuthorizedDaemonLogUser("mallory"));
+    }
+
+    /**
+     * daemon log access via the UI filter is granted by group membership 
alone.
+     */
+    @Test
+    public void testUserAllowedToAccessDaemonFileByGroupWhenFilterConfigured() 
{
+        Map<String, Object> stormConf = Utils.readStormConfig();
+
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(LOGS_GROUPS, Collections.singletonList("alice-group"));
+        conf.put(DaemonConfig.LOGVIEWER_FILTER, "someFilter");
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.singleton("alice-group")).when(authorizer).getUserGroups(anyString());
+
+        assertTrue(authorizer.isUserAllowedToAccessDaemonFile("alice"));
+    }
+
+    /**
+     * daemon log access consults the cluster level lists once a filter is 
configured.
+     */
+    @Test
+    public void daemonLogAuthorizationFailsWhenFilterConfigured() {
+        Map<String, Object> stormConf = Utils.readStormConfig();
+        Map<String, Object> conf = new HashMap<>(stormConf);
+        conf.put(LOGS_USERS, Collections.singletonList("alice"));
+
+        ResourceAuthorizer authorizer = spy(new ResourceAuthorizer(conf));
+
+        
doReturn(Collections.emptySet()).when(authorizer).getUserGroups(anyString());
+
+        assertTrue(authorizer.isUserAllowedToAccessDaemonFile("bob")); // no 
filter configured, allow anyone
+
+        conf.put(DaemonConfig.LOGVIEWER_FILTER, "someFilter");
+        assertTrue(authorizer.isUserAllowedToAccessDaemonFile("alice"));
+        assertFalse(authorizer.isUserAllowedToAccessDaemonFile("bob"));
+    }
 }

Reply via email to