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

xxyu pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit bcdbf5fb080da26b54908f2337f2f68105defc86
Author: ChenLiang.Lu <31469905+yab...@users.noreply.github.com>
AuthorDate: Mon Jan 16 18:13:03 2023 +0800

    KYLIN-5494 fix diag api security
---
 .../kylin/rest/controller/NBasicController.java    | 30 ++++++++++++++++
 .../kylin/rest/controller/NSystemController.java   | 12 ++++++-
 .../rest/controller/NSystemControllerTest.java     | 39 +++++++++++++++++++-
 .../kylin/rest/response/ServerExtInfoResponse.java | 42 ++++++++++++++++++++++
 .../kylin/rest/controller/NQueryController.java    |  8 ++++-
 .../rest/controller/NQueryControllerTest.java      | 16 +++++++++
 6 files changed, 144 insertions(+), 3 deletions(-)

diff --git 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NBasicController.java
 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NBasicController.java
index 9f7c1df5cd..914338294a 100644
--- 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NBasicController.java
+++ 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NBasicController.java
@@ -48,6 +48,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
@@ -66,6 +67,7 @@ import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
+import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
@@ -635,4 +637,32 @@ public class NBasicController {
             throw new KylinException(REQUEST_PARAMETER_EMPTY_OR_VALUE_EMPTY, 
fieldName);
         }
     }
+
+    public String decodeHost(String host) {
+        try {
+            if (StringUtils.isBlank(host) || host.startsWith("http://";)) {
+                return host;
+            }
+            return new String(Base64.decodeBase64(host), 
Charset.defaultCharset());
+        } catch (Exception e) {
+            logger.error("Failed to decode host, will use the original host 
name");
+        }
+        return host;
+    }
+
+    public String encodeHost(String host) {
+        try {
+            if (StringUtils.isBlank(host)) {
+                return host;
+            }
+            host = host.trim();
+            if (!host.toLowerCase().startsWith("http")) {
+                host = "http://"; + host;
+            }
+            return 
Base64.encodeBase64String(host.getBytes(Charset.defaultCharset()));
+        } catch (Exception e) {
+            logger.error("Failed to encode host, will use the original host 
name");
+        }
+        return host;
+    }
 }
diff --git 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NSystemController.java
 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NSystemController.java
index 26c02418f6..b700cdd33e 100644
--- 
a/src/common-server/src/main/java/org/apache/kylin/rest/controller/NSystemController.java
+++ 
b/src/common-server/src/main/java/org/apache/kylin/rest/controller/NSystemController.java
@@ -52,6 +52,7 @@ import org.apache.kylin.rest.request.QueryDiagPackageRequest;
 import org.apache.kylin.rest.response.DiagStatusResponse;
 import org.apache.kylin.rest.response.EnvelopeResponse;
 import org.apache.kylin.rest.response.MaintenanceModeResponse;
+import org.apache.kylin.rest.response.ServerExtInfoResponse;
 import org.apache.kylin.rest.response.ServerInfoResponse;
 import org.apache.kylin.rest.response.ServersResponse;
 import org.apache.kylin.rest.service.MaintenanceModeService;
@@ -135,6 +136,7 @@ public class NSystemController extends NBasicController {
     public EnvelopeResponse<String> getRemoteDumpDiagPackage(
             @RequestParam(value = "host", required = false) String host,
             @RequestBody DiagPackageRequest diagPackageRequest, final 
HttpServletRequest request) throws Exception {
+        host = decodeHost(host);
         if (StringUtils.isNotBlank(diagPackageRequest.getJobId())) {
             diagPackageRequest.setStart("");
             diagPackageRequest.setEnd("");
@@ -161,6 +163,7 @@ public class NSystemController extends NBasicController {
             @RequestParam(value = "host", required = false) String host,
             @RequestBody QueryDiagPackageRequest queryDiagPackageRequest, 
final HttpServletRequest request)
             throws Exception {
+        host = decodeHost(host);
         if (StringUtils.isEmpty(host)) {
             String uuid = 
systemService.dumpLocalQueryDiagPackage(queryDiagPackageRequest.getQueryId(),
                     queryDiagPackageRequest.getProject());
@@ -195,6 +198,7 @@ public class NSystemController extends NBasicController {
             @RequestParam(value = "host", required = false) String host, 
@RequestParam(value = "id") String id,
             @RequestParam(value = "project", required = false) String project, 
final HttpServletRequest request)
             throws Exception {
+        host = decodeHost(host);
         if (StringUtils.isEmpty(host)) {
             return systemService.getExtractorStatus(id, project);
         } else {
@@ -212,6 +216,7 @@ public class NSystemController extends NBasicController {
     public void remoteDownloadPackage(@RequestParam(value = "host", required = 
false) String host,
             @RequestParam(value = "id") String id, @RequestParam(value = 
"project", required = false) String project,
             final HttpServletRequest request, final HttpServletResponse 
response) throws Exception {
+        host = decodeHost(host);
         if (StringUtils.isEmpty(host)) {
             setDownloadResponse(systemService.getDiagPackagePath(id, project), 
MediaType.APPLICATION_OCTET_STREAM_VALUE,
                     response);
@@ -229,6 +234,7 @@ public class NSystemController extends NBasicController {
     @ResponseBody
     public EnvelopeResponse<String> remoteStopPackage(@RequestParam(value = 
"host", required = false) String host,
             @RequestParam(value = "id") String id, final HttpServletRequest 
request) throws Exception {
+        host = decodeHost(host);
         if (StringUtils.isEmpty(host)) {
             systemService.stopDiagTask(id);
             return new EnvelopeResponse<>(CODE_SUCCESS, "", "");
@@ -271,7 +277,11 @@ public class NSystemController extends NBasicController {
         val servers = clusterManager.getServers();
         response.setStatus(maintenanceModeService.getMaintenanceMode());
         if (ext) {
-            response.setServers(servers);
+            response.setServers(
+                servers.stream().map(server ->
+                    new ServerExtInfoResponse()
+                    .setServer(server)
+                    
.setSecretName(encodeHost(server.getHost()))).collect(Collectors.toList()));
         } else {
             
response.setServers(servers.stream().map(ServerInfoResponse::getHost).collect(Collectors.toList()));
         }
diff --git 
a/src/common-server/src/test/java/org/apache/kylin/rest/controller/NSystemControllerTest.java
 
b/src/common-server/src/test/java/org/apache/kylin/rest/controller/NSystemControllerTest.java
index 24dd2819b5..6deb6ce481 100644
--- 
a/src/common-server/src/test/java/org/apache/kylin/rest/controller/NSystemControllerTest.java
+++ 
b/src/common-server/src/test/java/org/apache/kylin/rest/controller/NSystemControllerTest.java
@@ -22,17 +22,23 @@ import static 
org.apache.kylin.common.constant.HttpConstant.HTTP_VND_APACHE_KYLI
 import java.time.LocalDateTime;
 import java.time.ZoneId;
 import java.time.temporal.ChronoUnit;
+import java.util.List;
 import java.util.Random;
 
 import org.apache.kylin.common.KylinConfigBase;
 import org.apache.kylin.common.util.JsonUtil;
-import org.apache.kylin.rest.constant.Constant;
 import org.apache.kylin.common.util.NLocalFileMetadataTestCase;
 import org.apache.kylin.junit.rule.TransactionExceptedException;
+import org.apache.kylin.rest.cluster.ClusterManager;
+import org.apache.kylin.rest.constant.Constant;
 import org.apache.kylin.rest.request.DiagPackageRequest;
 import org.apache.kylin.rest.request.DiagProgressRequest;
+import org.apache.kylin.rest.response.MaintenanceModeResponse;
+import org.apache.kylin.rest.response.ServerInfoResponse;
+import org.apache.kylin.rest.service.MaintenanceModeService;
 import org.apache.kylin.rest.service.SystemService;
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -48,6 +54,8 @@ import 
org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
 import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
 
+import com.google.common.collect.Lists;
+
 public class NSystemControllerTest extends NLocalFileMetadataTestCase {
     private static final String APPLICATION_JSON = HTTP_VND_APACHE_KYLIN_JSON;
 
@@ -62,6 +70,12 @@ public class NSystemControllerTest extends 
NLocalFileMetadataTestCase {
     @Rule
     public TransactionExceptedException thrown = 
TransactionExceptedException.none();
 
+    @Mock
+    private ClusterManager clusterManager;
+
+    @Mock
+    private MaintenanceModeService maintenanceModeService;
+
     @Before
     public void setUp() {
         createTestMetadata();
@@ -173,6 +187,15 @@ public class NSystemControllerTest extends 
NLocalFileMetadataTestCase {
                 .andExpect(MockMvcResultMatchers.status().isOk());
         
Mockito.verify(nSystemController).getRemotePackageStatus(Mockito.anyString(), 
Mockito.anyString(),
                 Mockito.anyString(), Mockito.any());
+        NBasicController controller = new NBasicController();
+        String encodeHost = controller.encodeHost("quickstart.cloudera:8088");
+        String decodeHost = controller.decodeHost(encodeHost);
+        controller.encodeHost("");
+        controller.decodeHost("");
+        
Assert.assertTrue(decodeHost.equalsIgnoreCase("http://quickstart.cloudera:8088";));
+        Assert.assertEquals(
+            "http://quickstart.cloudera:8088";,
+            controller.decodeHost("http://quickstart.cloudera:8088";));
     }
 
     @Test
@@ -272,4 +295,18 @@ public class NSystemControllerTest extends 
NLocalFileMetadataTestCase {
                 .andExpect(MockMvcResultMatchers.status().isOk());
         Mockito.verify(nSystemController).simulateInsertMeta(Mockito.anyInt(), 
Mockito.anyLong());
     }
+
+    @Test
+    public void testGetServer() throws Exception {
+        ServerInfoResponse response = new ServerInfoResponse();
+        response.setHost("172.168.1.1");
+        response.setMode("ALL");
+        List<ServerInfoResponse> result = Lists.newArrayList(response);
+        Mockito.when(clusterManager.getServers()).thenReturn(result);
+        Mockito.when(maintenanceModeService.getMaintenanceMode())
+            .thenReturn(new MaintenanceModeResponse(false, ""));
+        mockMvc.perform(MockMvcRequestBuilders.get("/api/system/servers")
+            .param("ext", 
"true").accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
+            .andExpect(MockMvcResultMatchers.status().isOk());
+    }
 }
diff --git 
a/src/common-service/src/main/java/org/apache/kylin/rest/response/ServerExtInfoResponse.java
 
b/src/common-service/src/main/java/org/apache/kylin/rest/response/ServerExtInfoResponse.java
new file mode 100644
index 0000000000..5214609b3c
--- /dev/null
+++ 
b/src/common-service/src/main/java/org/apache/kylin/rest/response/ServerExtInfoResponse.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.kylin.rest.response;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+public class ServerExtInfoResponse extends ServerInfoResponse {
+
+  private String secretName;
+
+  public ServerExtInfoResponse setServer(ServerInfoResponse response) {
+    this.setHost(response.getHost());
+    this.setMode(response.getMode());
+    return this;
+  }
+
+  public ServerExtInfoResponse setSecretName(String secretName) {
+    this.secretName = secretName;
+    return this;
+  }
+}
diff --git 
a/src/query-server/src/main/java/org/apache/kylin/rest/controller/NQueryController.java
 
b/src/query-server/src/main/java/org/apache/kylin/rest/controller/NQueryController.java
index 1c3ae4384b..35e2f9f86f 100644
--- 
a/src/query-server/src/main/java/org/apache/kylin/rest/controller/NQueryController.java
+++ 
b/src/query-server/src/main/java/org/apache/kylin/rest/controller/NQueryController.java
@@ -64,6 +64,7 @@ import org.apache.kylin.rest.request.SaveSqlRequest;
 import org.apache.kylin.rest.response.DataResult;
 import org.apache.kylin.rest.response.EnvelopeResponse;
 import org.apache.kylin.rest.response.SQLResponse;
+import org.apache.kylin.rest.response.ServerExtInfoResponse;
 import org.apache.kylin.rest.service.QueryService;
 import org.apache.kylin.rest.util.AclEvaluate;
 import 
org.apache.kylin.common.persistence.transaction.StopQueryBroadcastEventNotifier;
@@ -437,7 +438,12 @@ public class NQueryController extends NBasicController {
     public EnvelopeResponse<List> getServers(
             @RequestParam(value = "ext", required = false, defaultValue = 
"false") boolean ext) {
         if (ext) {
-            return new EnvelopeResponse<>(KylinException.CODE_SUCCESS, 
clusterManager.getServers(), "");
+            List<ServerExtInfoResponse> serverInfo =
+                clusterManager.getServers().stream().map(server ->
+                    new ServerExtInfoResponse()
+                        .setServer(server)
+                        
.setSecretName(encodeHost(server.getHost()))).collect(Collectors.toList());
+            return new EnvelopeResponse<>(KylinException.CODE_SUCCESS, 
serverInfo, "");
         } else {
             return new EnvelopeResponse<>(KylinException.CODE_SUCCESS,
                     
clusterManager.getServers().stream().map(ServerInfoResponse::getHost).collect(Collectors.toList()),
diff --git 
a/src/query-server/src/test/java/org/apache/kylin/rest/controller/NQueryControllerTest.java
 
b/src/query-server/src/test/java/org/apache/kylin/rest/controller/NQueryControllerTest.java
index 4307b8168f..cd39e4dda6 100644
--- 
a/src/query-server/src/test/java/org/apache/kylin/rest/controller/NQueryControllerTest.java
+++ 
b/src/query-server/src/test/java/org/apache/kylin/rest/controller/NQueryControllerTest.java
@@ -40,11 +40,13 @@ import 
org.apache.kylin.metadata.query.NativeQueryRealization;
 import org.apache.kylin.metadata.query.QueryHistory;
 import org.apache.kylin.metadata.query.QueryHistoryInfo;
 import org.apache.kylin.metadata.query.QueryHistoryRequest;
+import org.apache.kylin.rest.cluster.ClusterManager;
 import org.apache.kylin.rest.constant.Constant;
 import org.apache.kylin.rest.model.Query;
 import org.apache.kylin.rest.request.PrepareSqlRequest;
 import org.apache.kylin.rest.request.SQLRequest;
 import org.apache.kylin.rest.request.SaveSqlRequest;
+import org.apache.kylin.rest.response.ServerInfoResponse;
 import org.apache.kylin.rest.service.QueryCacheManager;
 import org.apache.kylin.rest.service.QueryHistoryService;
 import org.apache.kylin.rest.service.QueryService;
@@ -85,6 +87,8 @@ public class NQueryControllerTest extends 
NLocalFileMetadataTestCase {
     private QueryCacheManager queryCacheManager;
     @InjectMocks
     private NQueryController nQueryController = Mockito.spy(new 
NQueryController());
+    @Mock
+    private ClusterManager clusterManager;
 
     @Before
     public void setup() {
@@ -588,4 +592,16 @@ public class NQueryControllerTest extends 
NLocalFileMetadataTestCase {
                 
.andExpect(MockMvcResultMatchers.jsonPath("$.data[1]").value("MODEL2"));
         Mockito.verify(nQueryController).getQueryHistoryModels(PROJECT, 
request.getFilterModelName(), 3);
     }
+
+    @Test
+    public void testGetServers() throws Exception {
+        ServerInfoResponse response = new ServerInfoResponse();
+        response.setHost("172.168.1.1");
+        response.setMode("ALL");
+        List<ServerInfoResponse> result = Lists.newArrayList(response);
+        Mockito.when(clusterManager.getServers()).thenReturn(result);
+        mockMvc.perform(MockMvcRequestBuilders.get("/api/query/servers")
+            .param("ext", 
"true").accept(MediaType.parseMediaType(HTTP_VND_APACHE_KYLIN_JSON)))
+            .andExpect(MockMvcResultMatchers.status().isOk());
+    }
 }

Reply via email to