Skip to content

Commit cb8d29b

Browse files
authored
Change the worker log file permission only for download requests that are served (#8978)
1 parent c14bfaf commit cb8d29b

3 files changed

Lines changed: 88 additions & 6 deletions

File tree

storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandler.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
public class LogviewerLogDownloadHandler {
3131

32-
private WorkerLogs workerLogs;
3332
private final LogFileDownloader logFileDownloadHelper;
3433

3534
/**
@@ -43,8 +42,7 @@ public class LogviewerLogDownloadHandler {
4342
*/
4443
public LogviewerLogDownloadHandler(String logRoot, String daemonLogRoot, WorkerLogs workerLogs,
4544
ResourceAuthorizer resourceAuthorizer, StormMetricsRegistry metricsRegistry) {
46-
this.workerLogs = workerLogs;
47-
this.logFileDownloadHelper = new LogFileDownloader(logRoot, daemonLogRoot, resourceAuthorizer, metricsRegistry);
45+
this.logFileDownloadHelper = new LogFileDownloader(logRoot, daemonLogRoot, workerLogs, resourceAuthorizer, metricsRegistry);
4846
}
4947

5048
/**
@@ -57,7 +55,6 @@ public LogviewerLogDownloadHandler(String logRoot, String daemonLogRoot, WorkerL
5755
*
5856
*/
5957
public Response downloadLogFile(String host, String fileName, String user) throws IOException {
60-
workerLogs.setLogFilePermission(fileName);
6158
return logFileDownloadHelper.downloadFile(host, fileName, user, false);
6259
}
6360

storm-webapp/src/main/java/org/apache/storm/daemon/logviewer/utils/LogFileDownloader.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,23 @@ public class LogFileDownloader {
3636
private final Meter numFileDownloadExceptions;
3737
private final Path logRoot;
3838
private final Path daemonLogRoot;
39+
private final WorkerLogs workerLogs;
3940
private final ResourceAuthorizer resourceAuthorizer;
4041

4142
/**
4243
* Constructor.
4344
*
4445
* @param logRoot root worker log directory
4546
* @param daemonLogRoot root daemon log directory
47+
* @param workerLogs {@link WorkerLogs}
4648
* @param resourceAuthorizer {@link ResourceAuthorizer}
4749
* @param metricsRegistry The logviewer metrics registry
4850
*/
49-
public LogFileDownloader(String logRoot, String daemonLogRoot, ResourceAuthorizer resourceAuthorizer,
50-
StormMetricsRegistry metricsRegistry) {
51+
public LogFileDownloader(String logRoot, String daemonLogRoot, WorkerLogs workerLogs,
52+
ResourceAuthorizer resourceAuthorizer, StormMetricsRegistry metricsRegistry) {
5153
this.logRoot = Paths.get(logRoot).toAbsolutePath().normalize();
5254
this.daemonLogRoot = Paths.get(daemonLogRoot).toAbsolutePath().normalize();
55+
this.workerLogs = workerLogs;
5356
this.resourceAuthorizer = resourceAuthorizer;
5457
this.fileDownloadSizeDistMb = metricsRegistry.registerHistogram("logviewer:download-file-size-rounded-MB");
5558
this.numFileDownloadExceptions = metricsRegistry.registerMeter(ExceptionMeterNames.NUM_FILE_DOWNLOAD_EXCEPTIONS);
@@ -79,6 +82,10 @@ public Response downloadFile(String host, String fileName, String user, boolean
7982

8083
if (file.toFile().exists()) {
8184
if (isDaemon || resourceAuthorizer.isUserAllowedToAccessFile(user, fileName)) {
85+
if (!isDaemon) {
86+
//Only widen the permission of a worker log once the request is known to be served
87+
workerLogs.setLogFilePermission(fileName);
88+
}
8289
fileDownloadSizeDistMb.update(Math.round((double) file.toFile().length() / FileUtils.ONE_MB));
8390
String downloadedFileName;
8491
Path pathRelativeToRootDir = rootDir.relativize(file);

storm-webapp/src/test/java/org/apache/storm/daemon/logviewer/handler/LogviewerLogDownloadHandlerTest.java

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
import static org.hamcrest.CoreMatchers.not;
2525
import static org.hamcrest.CoreMatchers.nullValue;
2626
import static org.hamcrest.MatcherAssert.assertThat;
27+
import static org.mockito.ArgumentMatchers.anyString;
28+
import static org.mockito.Mockito.mock;
29+
import static org.mockito.Mockito.never;
30+
import static org.mockito.Mockito.verify;
31+
import static org.mockito.Mockito.when;
2732

2833
import com.google.common.net.HttpHeaders;
2934
import java.io.IOException;
@@ -121,6 +126,79 @@ public void testDownloadDaemonLogFilePathOutsideLogRoot() throws IOException {
121126
}
122127
}
123128

129+
@Test
130+
public void testDownloadLogFileUnauthorizedUserDoesNotChangeLogFilePermission() throws IOException {
131+
try (TmpPath rootPath = new TmpPath()) {
132+
Path daemonLogRoot = rootPath.getFile().toPath().resolve("logs");
133+
Path workerLogRoot = daemonLogRoot.resolve("workers-artifacts");
134+
Path file = workerLogRoot.resolve("topoA").resolve("1111").resolve("worker.log");
135+
Files.createDirectories(file.getParent());
136+
Files.createFile(file);
137+
138+
ResourceAuthorizer resourceAuthorizer = mock(ResourceAuthorizer.class);
139+
when(resourceAuthorizer.isUserAllowedToAccessFile(anyString(), anyString())).thenReturn(false);
140+
WorkerLogs workerLogs = mock(WorkerLogs.class);
141+
142+
LogviewerLogDownloadHandler handler = new LogviewerLogDownloadHandler(workerLogRoot.toString(),
143+
daemonLogRoot.toString(), workerLogs, resourceAuthorizer, new StormMetricsRegistry());
144+
145+
Response response = handler.downloadLogFile("host", "topoA/1111/worker.log", "user");
146+
147+
Utils.forceDelete(rootPath.toString());
148+
149+
assertThat(response.getStatus(), is(Response.Status.FORBIDDEN.getStatusCode()));
150+
verify(workerLogs, never()).setLogFilePermission(anyString());
151+
}
152+
}
153+
154+
@Test
155+
public void testDownloadLogFileAuthorizedUserSetsLogFilePermission() throws IOException {
156+
try (TmpPath rootPath = new TmpPath()) {
157+
Path daemonLogRoot = rootPath.getFile().toPath().resolve("logs");
158+
Path workerLogRoot = daemonLogRoot.resolve("workers-artifacts");
159+
Path file = workerLogRoot.resolve("topoA").resolve("1111").resolve("worker.log");
160+
Files.createDirectories(file.getParent());
161+
Files.createFile(file);
162+
163+
ResourceAuthorizer resourceAuthorizer = mock(ResourceAuthorizer.class);
164+
when(resourceAuthorizer.isUserAllowedToAccessFile(anyString(), anyString())).thenReturn(true);
165+
WorkerLogs workerLogs = mock(WorkerLogs.class);
166+
167+
LogviewerLogDownloadHandler handler = new LogviewerLogDownloadHandler(workerLogRoot.toString(),
168+
daemonLogRoot.toString(), workerLogs, resourceAuthorizer, new StormMetricsRegistry());
169+
170+
Response response = handler.downloadLogFile("host", "topoA/1111/worker.log", "user");
171+
172+
Utils.forceDelete(rootPath.toString());
173+
174+
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
175+
verify(workerLogs).setLogFilePermission("topoA/1111/worker.log");
176+
}
177+
}
178+
179+
@Test
180+
public void testDownloadDaemonLogFileDoesNotChangeLogFilePermission() throws IOException {
181+
try (TmpPath rootPath = new TmpPath()) {
182+
Path daemonLogRoot = rootPath.getFile().toPath().resolve("logs");
183+
Path workerLogRoot = daemonLogRoot.resolve("workers-artifacts");
184+
Path daemonFile = daemonLogRoot.resolve("nimbus.log");
185+
Files.createDirectories(workerLogRoot);
186+
Files.createFile(daemonFile);
187+
188+
WorkerLogs workerLogs = mock(WorkerLogs.class);
189+
190+
LogviewerLogDownloadHandler handler = new LogviewerLogDownloadHandler(workerLogRoot.toString(),
191+
daemonLogRoot.toString(), workerLogs, new ResourceAuthorizer(Utils.readStormConfig()), new StormMetricsRegistry());
192+
193+
Response response = handler.downloadDaemonLogFile("host", "nimbus.log", "user");
194+
195+
Utils.forceDelete(rootPath.toString());
196+
197+
assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode()));
198+
verify(workerLogs, never()).setLogFilePermission(anyString());
199+
}
200+
}
201+
124202
private LogviewerLogDownloadHandler createHandlerTraversalTests(Path rootPath) throws IOException {
125203
Path daemonLogRoot = rootPath.resolve("logs");
126204
Path fileOutsideDaemonRoot = rootPath.resolve("evil.sh");

0 commit comments

Comments
 (0)