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


##########
src/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java:
##########
@@ -0,0 +1,634 @@
+/*
+ * 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.TableWithComment;
+import org.apache.kylin.jdbc.json.TablesWithCommentResponse;
+import org.apache.kylin.jdbc.json.TableMetaStub.ColumnMetaStub;
+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 final static int POOL_MAX = 10;
+    private final static int POOL_MIN = 0;
+    private final static int RESPONSE_CODE_200 = 200;
+    private final static int RESPONSE_CODE_201 = 201;
+    private static final String APPLICATION = "application/json";
+    private static final String TIME_ZONE = "UTC";
+
+    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", "Basic " + authToken);

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



##########
src/jdbc/src/main/java/org/apache/kylin/jdbc/KylinClient.java:
##########
@@ -0,0 +1,634 @@
+/*
+ * 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.TableWithComment;
+import org.apache.kylin.jdbc.json.TablesWithCommentResponse;
+import org.apache.kylin.jdbc.json.TableMetaStub.ColumnMetaStub;
+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 final static int POOL_MAX = 10;
+    private final static int POOL_MIN = 0;
+    private final static int RESPONSE_CODE_200 = 200;
+    private final static int RESPONSE_CODE_201 = 201;
+    private static final String APPLICATION = "application/json";
+    private static final String TIME_ZONE = "UTC";
+
+    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);

Review Comment:
   ## Server hostnames should be verified during SSL/TLS connections
   
   <!--SONAR_ISSUE_KEY:AYSKSzxuqo1OziNXkSc0-->Enable server hostname 
verification on this SSL/TLS connection. <p>See more on <a 
href="https://sonarcloud.io/project/issues?id=apache_kylin&issues=AYSKSzxuqo1OziNXkSc0&open=AYSKSzxuqo1OziNXkSc0&pullRequest=2025";>SonarCloud</a></p>
   
   [Show more details](https://github.com/apache/kylin/security/code-scanning/2)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to