[ 
https://issues.apache.org/jira/browse/KYLIN-5297?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17636494#comment-17636494
 ] 

ASF GitHub Bot commented on KYLIN-5297:
---------------------------------------

github-code-scanning[bot] commented on code in PR #2025:
URL: https://github.com/apache/kylin/pull/2025#discussion_r1027653013


##########
src/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java:
##########
@@ -0,0 +1,647 @@
+/*
+ * 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.jdbc;
+
+import static org.apache.kylin.jdbc.LoggerUtils.entry;
+import static org.apache.kylin.jdbc.LoggerUtils.exit;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import javax.net.ssl.SSLContext;
+import javax.xml.bind.DatatypeConverter;
+
+import org.apache.calcite.avatica.AvaticaParameter;
+import org.apache.calcite.avatica.ColumnMetaData;
+import org.apache.calcite.avatica.ColumnMetaData.Rep;
+import org.apache.calcite.avatica.ColumnMetaData.ScalarType;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.config.Registry;
+import org.apache.http.config.RegistryBuilder;
+import org.apache.http.conn.socket.ConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.entity.ContentType;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
+import org.apache.http.ssl.SSLContexts;
+import org.apache.http.util.EntityUtils;
+import org.apache.kylin.jdbc.KylinMeta.KMetaCatalog;
+import org.apache.kylin.jdbc.KylinMeta.KMetaColumn;
+import org.apache.kylin.jdbc.KylinMeta.KMetaProject;
+import org.apache.kylin.jdbc.KylinMeta.KMetaSchema;
+import org.apache.kylin.jdbc.KylinMeta.KMetaTable;
+import org.apache.kylin.jdbc.json.GenericResponse;
+import org.apache.kylin.jdbc.json.PreparedQueryRequest;
+import org.apache.kylin.jdbc.json.SQLResponseStub;
+import org.apache.kylin.jdbc.json.StatementParameter;
+import org.apache.kylin.jdbc.json.TableMetaStub;
+import org.apache.kylin.jdbc.json.TableMetaStub.ColumnMetaStub;
+import org.apache.kylin.jdbc.json.TableWithComment;
+import org.apache.kylin.jdbc.json.TablesWithCommentResponse;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class KylinClient implements IRemoteClient {
+
+    // TODO: cannot support tableau
+
+    private static final Logger logger = 
LoggerFactory.getLogger(KylinClient.class);
+
+    private final KylinConnection conn;
+    private final Properties connProps;
+    private final CloseableHttpClient httpClient;
+    private final ObjectMapper jsonMapper;
+    private static final int POOL_MAX = 10;
+    private static final int POOL_MIN = 0;
+    private static final int RESPONSE_CODE_200 = 200;
+    private static final int RESPONSE_CODE_201 = 201;
+    private static final String APPLICATION = "application/json";
+    private static final String TIME_ZONE = "UTC";
+    private static final String AUTH_METHOD = "Basic ";
+
+    public KylinClient(KylinConnection conn) {
+        entry(logger);
+        this.conn = conn;
+        this.connProps = conn.getConnectionProperties();
+        this.httpClient = buildHttpClient();
+        this.jsonMapper = new ObjectMapper();
+        jsonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
+        exit(logger);
+    }
+
+    private CloseableHttpClient buildHttpClient() {
+        HttpClientBuilder builder = HttpClients.custom();
+
+        // network timeout
+        int timeout = Integer.parseInt(connProps.getProperty("timeout", "0"));
+        RequestConfig rconf = 
RequestConfig.custom().setConnectTimeout(timeout).setSocketTimeout(timeout).build();
+        builder.setDefaultRequestConfig(rconf);
+        logger.debug("use connection timeout " + timeout);
+
+        // SSL friendly
+        PoolingHttpClientConnectionManager cm;
+        if (isSSL()) {
+            try {
+                SSLContext sslContext = SSLContexts.custom()
+                        .loadTrustMaterial((TrustStrategy) (x509Certificates, 
s) -> true).build();
+                SSLConnectionSocketFactory sslsf = new 
SSLConnectionSocketFactory(sslContext, (s, sslSession) -> true);
+                Registry<ConnectionSocketFactory> r = 
RegistryBuilder.<ConnectionSocketFactory> create()
+                        .register("https", sslsf).build();
+                cm = new PoolingHttpClientConnectionManager(r);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
+            logger.debug("use SSL connection with optimistic trust");
+        } else {
+            cm = new PoolingHttpClientConnectionManager();
+            logger.debug("use non-SSL connection");
+        }
+
+        // connection pool
+        int pool = Integer.parseInt(connProps.getProperty("pool", "1"));
+        if (pool > POOL_MAX || pool < POOL_MIN) {
+            logger.debug("invalid 'pool', reset to default");
+            pool = 1;
+        }
+        if (pool == 0) {
+            logger.debug("use NO connection pool");
+        } else {
+            cm.setDefaultMaxPerRoute(pool);
+            cm.setMaxTotal(pool);
+            logger.debug("use connection pool size " + pool);
+        }
+
+        builder.setConnectionManager(cm);
+        return builder.build();
+    }
+
+    @SuppressWarnings("rawtypes")
+    public static Class convertType(int sqlType) {
+        Class result = Object.class;
+
+        switch (sqlType) {
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+                result = String.class;
+                break;
+            case Types.NUMERIC:
+            case Types.DECIMAL:
+                result = BigDecimal.class;
+                break;
+            case Types.BIT:
+            case Types.BOOLEAN:
+                result = Boolean.class;
+                break;
+            case Types.TINYINT:
+                result = Byte.class;
+                break;
+            case Types.SMALLINT:
+                result = Short.class;
+                break;
+            case Types.INTEGER:
+                result = Integer.class;
+                break;
+            case Types.BIGINT:
+                result = Long.class;
+                break;
+            case Types.REAL:
+            case Types.FLOAT:
+            case Types.DOUBLE:
+                result = Double.class;
+                break;
+            case Types.BINARY:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY:
+                result = Byte[].class;
+                break;
+            case Types.DATE:
+                result = Date.class;
+                break;
+            case Types.TIME:
+                result = Time.class;
+                break;
+            case Types.TIMESTAMP:
+                result = Timestamp.class;
+                break;
+            default:
+                //do nothing
+                break;
+        }
+
+        return result;
+    }
+
+    public Object wrapObject(String value, int sqlType) {
+        if (null == value) {
+            return null;
+        }
+
+        switch (sqlType) {
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+                return value;
+            case Types.NUMERIC:
+            case Types.DECIMAL:
+                return new BigDecimal(value);
+            case Types.BIT:
+            case Types.BOOLEAN:
+                return Boolean.parseBoolean(value);
+            case Types.TINYINT:
+                return Byte.valueOf(value);
+            case Types.SMALLINT:
+                return Short.valueOf(value);
+            case Types.INTEGER:
+                return Integer.parseInt(value);
+            case Types.BIGINT:
+                return Long.parseLong(value);
+            case Types.FLOAT:
+                return Float.parseFloat(value);
+            case Types.REAL:
+            case Types.DOUBLE:
+                return Double.parseDouble(value);
+            case Types.BINARY:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY:
+                return value.getBytes();
+            case Types.DATE:
+                return dateConverter(value);
+            case Types.TIME:
+                return timeConverter(value);
+            case Types.TIMESTAMP:
+                return timestampConverter(value);
+            default:
+                //do nothing
+                break;
+        }
+
+        return value;
+    }
+
+    private Date dateConverter(String value) {
+        try {
+            return new Date(parseDateTime(value, "yyyy-MM-dd"));
+        } catch (ParseException ex) {
+            logger.error("parse date failed!", ex);
+            return null;
+        }
+    }
+
+    private Time timeConverter(String value) {
+        try {
+            return new Time(parseDateTime(value, "HH:mm:ss"));
+        } catch (ParseException ex) {
+            logger.error("parse time failed!", ex);
+            return null;
+        }
+    }
+
+    private Timestamp timestampConverter(String value) {
+        String[] formats = new String[] { "yyyy-MM-dd HH:mm:ss.SSS", 
"yyyy-MM-dd HH:mm:ss" };
+        ParseException ex = null;
+        for (String format : formats) {
+            try {
+                return new Timestamp(parseDateTime(value, format));
+            } catch (ParseException e) {
+                ex = e;
+            }
+        }
+        logger.error("parse timestamp failed!", ex);
+        return null;
+    }
+
+    private long parseDateTime(String value, String format) throws 
ParseException {
+        SimpleDateFormat formatter = new SimpleDateFormat(format);
+        formatter.setTimeZone(TimeZone.getTimeZone(TIME_ZONE));
+        return formatter.parse(value).getTime();
+    }
+
+    private boolean isSSL() {
+        return Boolean.parseBoolean(connProps.getProperty("ssl", "false"));
+    }
+
+    private String baseUrl() {
+        return (isSSL() ? "https://"; : "http://";) + conn.getBaseUrl();
+    }
+
+    private void addHttpHeadersV2(HttpRequestBase method) {
+        String headerVal = "application/vnd.apache.kylin-v4-public+json, 
text/plain, */*";
+        method.addHeader("Accept", headerVal);
+        addCommonHttpHeaders(method);
+    }
+
+    private void addCommonHttpHeaders(HttpRequestBase method) {
+        method.addHeader("Content-Type", APPLICATION);
+        method.addHeader("User-Agent", "KylinJDBCDriver");
+        String authToken = connProps.getProperty("auth-token");
+        if (authToken == null || authToken.trim().isEmpty()) {
+            String username = connProps.getProperty("user");
+            String password = connProps.getProperty("password");
+            authToken = DatatypeConverter.printBase64Binary((username + ":" + 
password).getBytes());
+        }
+        method.addHeader("Authorization", AUTH_METHOD + authToken);

Review Comment:
   ## Basic authentication should not be used
   
   <!--SONAR_ISSUE_KEY:AYSZEmi7F37vEmXDYEmj-->Use a more secure method than 
basic authentication. <p>See more on <a 
href="https://sonarcloud.io/project/issues?id=apache_kylin&issues=AYSZEmi7F37vEmXDYEmj&open=AYSZEmi7F37vEmXDYEmj&pullRequest=2025";>SonarCloud</a></p>
   
   [Show more details](https://github.com/apache/kylin/security/code-scanning/3)





> Add Kylin5 Jdbc module
> ----------------------
>
>                 Key: KYLIN-5297
>                 URL: https://issues.apache.org/jira/browse/KYLIN-5297
>             Project: Kylin
>          Issue Type: New Feature
>    Affects Versions: 5.0-alpha
>            Reporter: mukvin
>            Assignee: mukvin
>            Priority: Major
>             Fix For: 5.0-alpha
>
>
> Add Kylin JDBC module for kylin 5



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to