Skip to content

Commit

Permalink
[Feature] [JDBC-Engine] Optimize JDBC parameter acquisition logic. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
CCweixiao committed Sep 5, 2022
1 parent 3261329 commit 6bef239
Show file tree
Hide file tree
Showing 3 changed files with 209 additions and 31 deletions.
Expand Up @@ -20,6 +20,7 @@
import org.apache.linkis.hadoop.common.utils.KerberosUtils;
import org.apache.linkis.manager.engineplugin.jdbc.constant.JDBCEngineConnConstant;
import org.apache.linkis.manager.engineplugin.jdbc.exception.JDBCParamsIllegalException;
import org.apache.linkis.manager.engineplugin.jdbc.utils.JdbcParamUtils;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -42,7 +43,6 @@
import static org.apache.linkis.manager.engineplugin.jdbc.JdbcAuthType.*;

public class ConnectionManager {

private static final Logger LOG = LoggerFactory.getLogger(ConnectionManager.class);

private final Map<String, DataSource> dataSourceFactories;
Expand Down Expand Up @@ -109,27 +109,20 @@ public void close() {

protected DataSource buildDataSource(String dbUrl, Map<String, String> properties)
throws JDBCParamsIllegalException {

String driverClassName =
JDBCPropertiesParser.getString(properties, JDBCEngineConnConstant.JDBC_DRIVER, "");

if (StringUtils.isBlank(driverClassName)) {
LOG.error("The driver class name is not empty.");
throw new JDBCParamsIllegalException("The driver class name is not empty.");
}

String username =
JDBCPropertiesParser.getString(properties, JDBCEngineConnConstant.JDBC_USERNAME, "");
String password =
JDBCPropertiesParser.getString(properties, JDBCEngineConnConstant.JDBC_PASSWORD, "");
String username = JdbcParamUtils.getJdbcUsername(properties);
String password = JdbcParamUtils.getJdbcPassword(properties);
JdbcAuthType jdbcAuthType = getJdbcAuthType(properties);
switch (jdbcAuthType) {
case USERNAME:
if (StringUtils.isBlank(username)) {
throw new JDBCParamsIllegalException("The jdbc username is not empty.");
}
if (StringUtils.isBlank(password)) {
throw new JDBCParamsIllegalException("The jdbc password is not empty.");
}
LOG.info("The jdbc auth type is username and password.");
break;
case SIMPLE:
LOG.info("The jdbc auth type is simple.");
Expand Down Expand Up @@ -294,28 +287,12 @@ private String getJdbcUrl(Map<String, String> properties) throws SQLException {
if (StringUtils.isBlank(url)) {
throw new SQLException(JDBCEngineConnConstant.JDBC_URL + " is not empty.");
}
url = clearJDBCUrl(url);
validateJDBCUrl(url);
url = JdbcParamUtils.clearJdbcUrl(url);
url = JdbcParamUtils.filterJdbcUrl(url);
JdbcParamUtils.validateJdbcUrl(url);
return url.trim();
}

private String clearJDBCUrl(String url) {
if (url.startsWith("\"") && url.endsWith("\"")) {
url = url.trim();
return url.substring(1, url.length() - 1);
}
return url;
}

private void validateJDBCUrl(String url) {
if (StringUtils.isEmpty(url)) {
throw new NullPointerException(JDBCEngineConnConstant.JDBC_URL + " cannot be null.");
}
if (!url.matches("jdbc:\\w+://\\S+:[0-9]{2,6}(/\\S*)?") && !url.startsWith("jdbc:h2")) {
throw new IllegalArgumentException("JDBC url format error!" + url);
}
}

private String appendProxyUserToJDBCUrl(
String jdbcUrl, String execUser, Map<String, String> properties) {
StringBuilder jdbcUrlSb = new StringBuilder(jdbcUrl);
Expand Down
@@ -0,0 +1,119 @@
/*
* 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.linkis.manager.engineplugin.jdbc.utils;

import org.apache.linkis.manager.engineplugin.jdbc.JDBCPropertiesParser;
import org.apache.linkis.manager.engineplugin.jdbc.constant.JDBCEngineConnConstant;
import org.apache.linkis.manager.engineplugin.jdbc.exception.JDBCParamsIllegalException;

import org.apache.commons.lang3.StringUtils;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JdbcParamUtils {
private static final Logger LOG = LoggerFactory.getLogger(JdbcParamUtils.class);
private static final String JDBC_MATCH_REGEX = "jdbc:\\w+://\\S+:[0-9]{2,6}(/\\S*)?";
private static final String JDBC_H2_PROTOCOL = "jdbc:h2";

private static final String JDBC_MYSQL_PROTOCOL = "jdbc:mysql";

private static final String SENSITIVE_PARAM = "autoDeserialize=true";
private static final String AUTO_DESERIALIZE = "autoDeserialize";

private static final String APPEND_PARAMS =
"allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false";

private static final char AND_SYMBOL = '&';

private static final String QUOTATION_MARKS = "\"";

private static final char QUESTION_MARK = '?';

public static String clearJdbcUrl(String url) {
if (url.startsWith(QUOTATION_MARKS) && url.endsWith(QUOTATION_MARKS)) {
url = url.trim();
return url.substring(1, url.length() - 1);
}
return url;
}

public static void validateJdbcUrl(String url) {
if (!url.matches(JDBC_MATCH_REGEX) && !url.startsWith(JDBC_H2_PROTOCOL)) {
throw new IllegalArgumentException("JDBC url format error!" + url);
}
}

public static String filterJdbcUrl(String url) {
// temporarily filter only mysql jdbc url.
if (!url.startsWith(JDBC_MYSQL_PROTOCOL)) {
return url;
}
if (url.contains(SENSITIVE_PARAM)) {
int index = url.indexOf(SENSITIVE_PARAM);
String tmp = SENSITIVE_PARAM;
if (url.charAt(index - 1) == AND_SYMBOL) {
tmp = AND_SYMBOL + tmp;
} else if (url.charAt(index + 1) == AND_SYMBOL) {
tmp = tmp + AND_SYMBOL;
}
LOG.warn("Sensitive param: {} in jdbc url is filtered.", tmp);
url = url.replace(tmp, "");
}
if (url.endsWith(String.valueOf(QUESTION_MARK))) {
url = url + APPEND_PARAMS;
} else if (url.lastIndexOf(QUESTION_MARK) < 0) {
url = url + QUESTION_MARK + APPEND_PARAMS;
} else {
url = url + AND_SYMBOL + APPEND_PARAMS;
}
LOG.info("The filtered jdbc url is: {}", url);
return url;
}

public static String getJdbcUsername(Map<String, String> properties)
throws JDBCParamsIllegalException {
String username =
JDBCPropertiesParser.getString(properties, JDBCEngineConnConstant.JDBC_USERNAME, "");
if (StringUtils.isBlank(username)) {
throw new JDBCParamsIllegalException("The jdbc username is not empty.");
}
if (username.contains(AUTO_DESERIALIZE)) {
LOG.warn("Sensitive param : {} in username field is filtered.", AUTO_DESERIALIZE);
username = username.replace(AUTO_DESERIALIZE, "");
}
LOG.info("The jdbc username is: {}", username);
return username;
}

public static String getJdbcPassword(Map<String, String> properties)
throws JDBCParamsIllegalException {
String password =
JDBCPropertiesParser.getString(properties, JDBCEngineConnConstant.JDBC_PASSWORD, "");
if (StringUtils.isBlank(password)) {
throw new JDBCParamsIllegalException("The jdbc password is not empty.");
}
if (password.contains(AUTO_DESERIALIZE)) {
LOG.warn("Sensitive param : {} in password field is filtered", AUTO_DESERIALIZE);
password = password.replace(AUTO_DESERIALIZE, "");
}
return password;
}
}
@@ -0,0 +1,82 @@
/*
* 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.linkis.manager.engineplugin.jdbc.utils;

import org.apache.linkis.manager.engineplugin.jdbc.constant.JDBCEngineConnConstant;
import org.apache.linkis.manager.engineplugin.jdbc.exception.JDBCParamsIllegalException;

import java.util.HashMap;
import java.util.Map;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

public class JdbcParamUtilsTest {
@Test
@DisplayName("testFilterJdbcUrl")
public void testFilterJdbcUrl() {
String url = "jdbc:mysql://localhost:3306/db_name";
url = JdbcParamUtils.filterJdbcUrl(url);
Assertions.assertEquals(
"jdbc:mysql://localhost:3306/db_name?allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
url);

url = "jdbc:mysql://localhost:3306/db_name?p1=v1";
url = JdbcParamUtils.filterJdbcUrl(url);
Assertions.assertEquals(
"jdbc:mysql://localhost:3306/db_name?p1=v1&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
url);

url = "jdbc:mysql://localhost:3306/db_name?autoDeserialize=true";
url = JdbcParamUtils.filterJdbcUrl(url);
Assertions.assertEquals(
"jdbc:mysql://localhost:3306/db_name?allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
url);

url = "jdbc:mysql://localhost:3306/db_name?p1=v1&autoDeserialize=true";
url = JdbcParamUtils.filterJdbcUrl(url);
Assertions.assertEquals(
"jdbc:mysql://localhost:3306/db_name?p1=v1&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
url);

url = "jdbc:mysql://localhost:3306/db_name?p1=v1&autoDeserialize=true&p2=v2";
url = JdbcParamUtils.filterJdbcUrl(url);
Assertions.assertEquals(
"jdbc:mysql://localhost:3306/db_name?p1=v1&p2=v2&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
url);
}

@Test
@DisplayName("testGetJdbcUsername")
public void testGetJdbcUsername() throws JDBCParamsIllegalException {
Map<String, String> properties = new HashMap<>();
properties.put(JDBCEngineConnConstant.JDBC_USERNAME, "test123?autoDeserialize=true");
String username = JdbcParamUtils.getJdbcUsername(properties);
Assertions.assertEquals("test123?=true", username);
}

@Test
@DisplayName("testGetJdbcPassword")
public void testGetJdbcPassword() throws JDBCParamsIllegalException {
Map<String, String> properties = new HashMap<>();
properties.put(JDBCEngineConnConstant.JDBC_USERNAME, "test_pwd?autoDeserialize=true");
String password = JdbcParamUtils.getJdbcUsername(properties);
Assertions.assertEquals("test_pwd?=true", password);
}
}

0 comments on commit 6bef239

Please sign in to comment.