Skip to content

Commit 8251dc4

Browse files
authored
[ZEPPELIN-6366] Separate WebSocket max message size into a dedicated REST API
### What is this PR for? Currently, configuration data is fetched through both REST API and WebSocket channels. However, the WebSocket path does not perform permission checks, and the only required data from it is the WebSocket max message size. I extracted the websocket max message size field into a dedicated REST API, to improve security and simplify configuration handling. ### What type of PR is it? Improvement ### Todos * [ ] #5060 ### What is the Jira issue? * Open an issue on Jira https://issues.apache.org/jira/browse/ZEPPELIN/6366 ### How should this be tested? - Check the configuration page (/configuration) - Check the notebook page (/notebook/{notebook_id}) ### Screenshots (if appropriate) ### Questions: * Does the license files need to update? N * Is there breaking changes for older versions? Y * Does this needs documentation? N Closes #5099 from seung-00/feature/ZEPPELIN-6366. Signed-off-by: ChanHo Lee <chanholee@apache.org>
1 parent a041703 commit 8251dc4

11 files changed

Lines changed: 82 additions & 39 deletions

File tree

conf/shiro.ini.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ admin = *
123123
/api/interpreter/setting/restart/** = authc
124124
/api/interpreter/** = authc, roles[admin]
125125
/api/notebook-repositories/** = authc, roles[admin]
126+
/api/configurations/client/** = anon
126127
/api/configurations/** = authc, roles[admin]
127128
/api/credential/** = authc, roles[admin]
128129
/api/admin/** = authc, roles[admin]

zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ void testAngularRunParagraph() throws Exception {
334334

335335
// Click on 1 paragraph to trigger z.runParagraph() function
336336

337+
ZeppelinITUtils.sleep(1000, false);
337338
clickAndWait(By.xpath(getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
338339

339340
waitForParagraph(2, "FINISHED");

zeppelin-server/src/main/java/org/apache/zeppelin/rest/ConfigurationsRestApi.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,21 @@ public ConfigurationsRestApi(
4646
this.configurationService = configurationService;
4747
}
4848

49+
@GET
50+
@Path("client")
51+
@ZeppelinApi
52+
public Response getClientConfigurations() {
53+
try {
54+
int maxMessageSize = configurationService.getWsMaxMessageSize();
55+
56+
return new JsonResponse<>(Status.OK, "", Map.of(
57+
"wsMaxMessageSize", maxMessageSize
58+
)).build();
59+
} catch (Exception e) {
60+
return new JsonResponse<>(Status.INTERNAL_SERVER_ERROR, "Fail to get max message size", e).build();
61+
}
62+
}
63+
4964
@GET
5065
@Path("all")
5166
@ZeppelinApi

zeppelin-server/src/main/java/org/apache/zeppelin/service/ConfigurationService.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ public Map<String, String> getAllProperties(ServiceContext context,
4343
return properties;
4444
}
4545

46+
public int getWsMaxMessageSize() {
47+
return Integer.parseInt(zConf.getWebsocketMaxTextMessageSize());
48+
}
49+
4650
public Map<String, String> getPropertiesWithPrefix(String prefix,
4751
ServiceContext context,
4852
ServiceCallback<Map<String, String>> callback)

zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-common.interface.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export interface Ticket {
2929
roles: string;
3030
}
3131

32+
export interface ClientConfigurations {
33+
wsMaxMessageSize: number;
34+
}
35+
3236
export interface ConfigurationsInfo {
3337
configurations: {
3438
'zeppelin.war.tempdir': string;

zeppelin-web-angular/src/app/pages/workspace/notebook/action-bar/action-bar.component.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,14 @@ import { NzModalService } from 'ng-zorro-antd/modal';
2929
import { MessageListener, MessageListenersManager } from '@zeppelin/core';
3030
import { TRASH_FOLDER_ID_TOKEN } from '@zeppelin/interfaces';
3131
import { MessageReceiveDataTypeMap, Note, OP, RevisionListItem } from '@zeppelin/sdk';
32-
import { MessageService, NotebookService, NoteStatusService, SaveAsService, TicketService } from '@zeppelin/services';
32+
import {
33+
ConfigurationService,
34+
MessageService,
35+
NotebookService,
36+
NoteStatusService,
37+
SaveAsService,
38+
TicketService
39+
} from '@zeppelin/services';
3340
import { NoteCreateComponent, ShortcutComponent } from '@zeppelin/share';
3441

3542
@Component({
@@ -189,11 +196,12 @@ export class NotebookActionBarComponent extends MessageListenersManager implemen
189196
});
190197
}
191198

192-
exportNote() {
199+
async exportNote() {
193200
if (!this.ticketService.configuration) {
194201
throw new Error('Configuration is not loaded');
195202
}
196-
const sizeLimit = +this.ticketService.configuration['zeppelin.websocket.max.text.message.size'];
203+
204+
const sizeLimit = await this.configurationService.fetchWsMaxMessageSize();
197205
const jsonContent = JSON.stringify(this.note);
198206
if (jsonContent.length > sizeLimit) {
199207
this.nzModalService.confirm({
@@ -324,6 +332,7 @@ export class NotebookActionBarComponent extends MessageListenersManager implemen
324332
@Inject(TRASH_FOLDER_ID_TOKEN) public TRASH_FOLDER_ID: string,
325333
private nzModalService: NzModalService,
326334
private ticketService: TicketService,
335+
private configurationService: ConfigurationService,
327336
private nzMessageService: NzMessageService,
328337
private router: Router,
329338
private cdr: ChangeDetectorRef,

zeppelin-web-angular/src/app/services/configuration.service.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
import { HttpClient } from '@angular/common/http';
1414
import { Injectable } from '@angular/core';
1515

16+
import { Observable } from 'rxjs';
17+
import { ClientConfigurations } from '@zeppelin/sdk';
18+
import { BaseUrlService } from '@zeppelin/services/base-url.service';
1619
import { BaseRest } from './base-rest';
17-
import { BaseUrlService } from './base-url.service';
1820

1921
@Injectable({
2022
providedIn: 'root'
@@ -27,7 +29,13 @@ export class ConfigurationService extends BaseRest {
2729
super(baseUrlService);
2830
}
2931

30-
getAll() {
32+
fetchWsMaxMessageSize(): Promise<number> {
33+
const configurations = this.http.get<ClientConfigurations>(this.restUrl`/configurations/client`);
34+
35+
return configurations.toPromise().then(config => config.wsMaxMessageSize);
36+
}
37+
38+
getAll(): Observable<{ [p: string]: string }> {
3139
return this.http.get<{ [key: string]: string }>(this.restUrl`/configurations/all`);
3240
}
3341
}

zeppelin-web-angular/src/app/share/note-import/note-import.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<p class="ant-upload-text">Click or drag JSON file to this area to upload</p>
2929
<p class="ant-upload-hint">
3030
JSON file size cannot exceed
31-
<strong class="tips warning">{{ maxLimit | humanizeBytes }}</strong>
31+
<strong class="tips warning">{{ wsMaxLimit | humanizeBytes }}</strong>
3232
</p>
3333
</nz-upload>
3434
</nz-tab>

zeppelin-web-angular/src/app/share/note-import/note-import.component.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,8 @@
1212

1313
import { HttpClient } from '@angular/common/http';
1414
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
15-
import { MessageService, TicketService } from '@zeppelin/services';
15+
import { ConfigurationService, MessageService, TicketService } from '@zeppelin/services';
1616

17-
import { get } from 'lodash';
1817
import { NzModalRef } from 'ng-zorro-antd/modal';
1918
import { NzUploadFile } from 'ng-zorro-antd/upload';
2019

@@ -32,7 +31,7 @@ export class NoteImportComponent extends MessageListenersManager implements OnIn
3231
importUrl?: string;
3332
errorText?: string;
3433
importLoading = false;
35-
maxLimit = get(this.ticketService.configuration, ['zeppelin.websocket.max.text.message.size'], null);
34+
wsMaxLimit?: number;
3635

3736
@MessageListener(OP.IMPORT_NOTE)
3837
noteImported(_: MessageReceiveDataTypeMap[OP.IMPORT_NOTE]) {
@@ -59,7 +58,7 @@ export class NoteImportComponent extends MessageListenersManager implements OnIn
5958

6059
beforeUpload = (file: NzUploadFile): boolean => {
6160
this.errorText = '';
62-
if (file.size !== undefined && this.maxLimit && file.size > Number.parseInt(this.maxLimit, 10)) {
61+
if (file.size !== undefined && this.wsMaxLimit && file.size > this.wsMaxLimit) {
6362
this.errorText = 'File size limit Exceeded!';
6463
} else {
6564
const reader = new FileReader();
@@ -102,12 +101,15 @@ export class NoteImportComponent extends MessageListenersManager implements OnIn
102101
constructor(
103102
public messageService: MessageService,
104103
private ticketService: TicketService,
104+
private configurationService: ConfigurationService,
105105
private cdr: ChangeDetectorRef,
106106
private nzModalRef: NzModalRef,
107107
private httpClient: HttpClient
108108
) {
109109
super(messageService);
110110
}
111111

112-
ngOnInit() {}
112+
async ngOnInit() {
113+
this.wsMaxLimit = await this.configurationService.fetchWsMaxMessageSize();
114+
}
113115
}

zeppelin-web/src/app/notebook/notebook.controller.js

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -267,30 +267,29 @@ function NotebookCtrl($scope, $route, $routeParams, $location, $rootScope,
267267
return note && note.path ? note.path.split('/')[1] === TRASH_FOLDER_ID : false;
268268
};
269269

270-
// Export notebook
271-
let limit = 0;
270+
$scope.exportNote = function() {
271+
$http.get(baseUrlSrv.getRestApiBase() + '/configurations/client').then(function(response) {
272+
const limit = response.data.body.wsMessageMaxSize;
272273

273-
websocketMsgSrv.listConfigurations();
274-
$scope.$on('configurationsInfo', function(scope, event) {
275-
limit = event.configurations['zeppelin.websocket.max.text.message.size'];
276-
});
274+
let jsonContent = JSON.stringify($scope.note, null, 2);
277275

278-
$scope.exportNote = function() {
279-
let jsonContent = JSON.stringify($scope.note, null, 2);
280-
if (jsonContent.length > limit) {
281-
BootstrapDialog.confirm({
282-
closable: true,
283-
title: 'Note size exceeds importable limit (' + limit + ')',
284-
message: 'Do you still want to export this note?',
285-
callback: function(result) {
286-
if (result) {
287-
saveAsService.saveAs(jsonContent, $scope.note.name + '_' + $scope.note.id, 'zpln');
288-
}
289-
},
290-
});
291-
} else {
292-
saveAsService.saveAs(jsonContent, $scope.note.name + '_' + $scope.note.id, 'zpln');
293-
}
276+
if (jsonContent.length > limit) {
277+
BootstrapDialog.confirm({
278+
closable: true,
279+
title: 'Note size exceeds importable limit (' + limit + ')',
280+
message: 'Do you still want to export this note?',
281+
callback: function(result) {
282+
if (result) {
283+
saveAsService.saveAs(jsonContent, $scope.note.name + '_' + $scope.note.id, 'zpln');
284+
}
285+
},
286+
});
287+
} else {
288+
saveAsService.saveAs(jsonContent, $scope.note.name + '_' + $scope.note.id, 'zpln');
289+
}
290+
}).catch(function(err) {
291+
console.error('Error while fetching max message size', err);
292+
});
294293
};
295294

296295
// Export nbformat

0 commit comments

Comments
 (0)