From 0dc75fee64940295fa44ef8d6d233f8c6bb8fbd9 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 14:08:47 +0800 Subject: [PATCH 01/30] implement JDBC Authentication Method --- .../org/apache/kyuubi/config/KyuubiConf.scala | 45 ++++ .../service/authentication/AuthMethods.scala | 2 +- .../AuthenticationProviderFactory.scala | 1 + .../JdbcAuthenticationProviderImpl.scala | 200 ++++++++++++++++++ 4 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala index da1d96cf363..88553f1ac3d 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala @@ -645,6 +645,51 @@ object KyuubiConf { .stringConf .createWithDefault("uid") + val AUTHENTICATION_JDBC_PASSWORD_DRIVER: OptionalConfigEntry[String] = + buildConf("kyuubi.authentication.jdbc.driver.class") + .doc("Driver class name for JDBC Password Authentication Provider.") + .version("1.6.0") + .stringConf + .createOptional + + val AUTHENTICATION_JDBC_URL: OptionalConfigEntry[String] = + buildConf("kyuubi.authentication.jdbc.url") + .doc("JDBC URL for JDBC Password Authentication Provider.") + .version("1.6.0") + .stringConf + .createOptional + + val AUTHENTICATION_JDBC_PASSWORD_USERNAME: OptionalConfigEntry[String] = + buildConf("kyuubi.authentication.jdbc.username") + .doc("Database username for JDBC Password Authentication Provider.") + .version("1.6.0") + .stringConf + .createOptional + + val AUTHENTICATION_JDBC_PASSWORD_PASSWORD: OptionalConfigEntry[String] = + buildConf("kyuubi.authentication.jdbc.password") + .doc("Database password for JDBC Password Authentication Provider.") + .version("1.6.0") + .stringConf + .createOptional + + val AUTHENTICATION_JDBC_PASSWORD_QUERY: OptionalConfigEntry[String] = + buildConf("kyuubi.authentication.jdbc.query") + .doc("Query SQL template with placeholders " + + "for JDBC Password Authentication Provider to execute." + + "Available placeholders are: " + + "Don't quote the placeholders, query will be processed as prepared statement, " + + "while username and password will be passed as parameters." + + "For example: SELECT 1 FROM auth_table WHERE user=${username} AND " + + "passwd=MD5(CONCAT(salt,${password})); " + + "Will be prepared as: SELECT 1 FROM auth_table " + + "WHERE user=? AND passwd=MD5(CONCAT(salt,?)); ") + .version("1.6.0") + .stringConf + .createOptional + val DELEGATION_KEY_UPDATE_INTERVAL: ConfigEntry[Long] = buildConf("kyuubi.delegation.key.update.interval") .doc("unused yet") diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthMethods.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthMethods.scala index fcbf39f699a..54c95acbbb3 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthMethods.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthMethods.scala @@ -19,5 +19,5 @@ package org.apache.kyuubi.service.authentication object AuthMethods extends Enumeration { type AuthMethod = Value - val NONE, LDAP, CUSTOM = Value + val NONE, LDAP, JDBC, CUSTOM = Value } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthenticationProviderFactory.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthenticationProviderFactory.scala index 8210c83c60d..ffdd9b8bb90 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthenticationProviderFactory.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthenticationProviderFactory.scala @@ -44,6 +44,7 @@ object AuthenticationProviderFactory { conf: KyuubiConf): PasswdAuthenticationProvider = method match { case AuthMethods.NONE => new AnonymousAuthenticationProviderImpl case AuthMethods.LDAP => new LdapAuthenticationProviderImpl(conf) + case AuthMethods.JDBC => new JdbcAuthenticationProviderImpl(conf) case AuthMethods.CUSTOM => val className = conf.get(KyuubiConf.AUTHENTICATION_CUSTOM_CLASS) if (className.isEmpty) { diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala new file mode 100644 index 00000000000..7515a43c308 --- /dev/null +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -0,0 +1,200 @@ +/* + * 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.kyuubi.service.authentication + + +import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet, Statement} +import javax.security.sasl.AuthenticationException + +import org.apache.commons.lang3.StringUtils + +import org.apache.kyuubi.Logging +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.config.KyuubiConf._ + +class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticationProvider + with Logging { + + private val dbDriver = conf.get(AUTHENTICATION_JDBC_PASSWORD_DRIVER).map(s => s.trim).orNull + private val dbUrl = conf.get(AUTHENTICATION_JDBC_URL).map(s => s.trim).orNull + private val dbUserName = conf.get(AUTHENTICATION_JDBC_PASSWORD_USERNAME).map(s => s.trim).orNull + private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD_PASSWORD).map(s => s.trim).orNull + private val querySql = conf.get(AUTHENTICATION_JDBC_PASSWORD_QUERY).map(s => s.trim).orNull + + private val SQL_PLACEHOLDER_REGEX = """\$\{.+?}""".r + private val USERNAME_SQL_PLACEHOLDER = "${username}" + private val PASSWORD_SQL_PLACEHOLDER = "${password}" + + /** + * The authenticate method is called by the Kyuubi Server authentication layer + * to authenticate users for their requests. + * If a user is to be granted, return nothing/throw nothing. + * When a user is to be disallowed, throw an appropriate [[AuthenticationException]]. + * + * @param user The username received over the connection request + * @param password The password received over the connection request + * @throws AuthenticationException When a user is found to be invalid by the implementation + */ + @throws[AuthenticationException] + override def authenticate(user: String, password: String): Unit = { + if (StringUtils.isBlank(user)) { + throw new AuthenticationException(s"Error validating, user is null" + + s" or contains blank space") + } + + if (StringUtils.isBlank(password)) { + throw new AuthenticationException(s"Error validating, password is null" + + s" or contains blank space") + } + + def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" + + debug(configLog("Driver Class", dbDriver)) + debug(configLog("JDBC URL", dbUrl)) + debug(configLog("Database Username", dbUserName)) + debug(configLog("Database Password", dbPassword)) + debug(configLog("Query SQL", querySql)) + + // Check if JDBC parameters valid + if (StringUtils.isBlank(dbDriver) || + StringUtils.isBlank(dbUrl) || + StringUtils.isBlank(dbUserName) || + StringUtils.isBlank(dbPassword)) { + error("User auth Database has not been configured!") + throw new IllegalArgumentException("User auth Database has not been configured!") + } + + // Check Query SQL + if (StringUtils.isBlank(querySql)) { + error("Query SQL not configured!") + throw new IllegalArgumentException("Query SQL not configured!") + } + if (!querySql.toLowerCase().startsWith("select")) { // only allow select query sql + error("Query SQL must start with \"select\"!") + throw new IllegalArgumentException("Query SQL must start with \"select\"!"); + } + + // Load Driver Class + try { + Class.forName(dbDriver) + } catch { + case e: ClassNotFoundException => + error(s"Driver class not found: $dbDriver") + throw e; + } + + var connection: Connection = null + var queryStatement: PreparedStatement = null + + try { + connection = DriverManager.getConnection(dbUrl, dbUserName, dbPassword) + + // Replace placeholders by "?" and prepare the statement + queryStatement = connection.prepareStatement(getPreparedSql(querySql)) + + // Extract placeholder list and use its order to pass parameters + val placeholderList: List[String] = getPlaceholderList(querySql) + for (i <- placeholderList.indices) { + val param = placeholderList(i) match { + case USERNAME_SQL_PLACEHOLDER => user + case PASSWORD_SQL_PLACEHOLDER => password + case otherPlaceholder => + error(s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") + throw new IllegalStateException( + s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") + } + + queryStatement.setString(i + 1, param) + } + + // Client side limit 1 + queryStatement.setMaxRows(1) + + val resultSet = queryStatement.executeQuery() + + if (resultSet == null || !resultSet.next()) { + // Auth failed + throw new AuthenticationException(s"Password does not match or no such user. user:" + + s" $user , password length: ${password.length}") + } + + // Auth passed + + } catch { + case e: AuthenticationException => + throw e + case e: Exception => + error("Cannot get user info", e); + throw e + } finally { + closeDbConnection(connection, queryStatement) + } + } + + /** + * Extract all placeholders from query and put them into a list. + * + * @param sql + * @return + */ + private def getPlaceholderList(sql: String): List[String] = { + SQL_PLACEHOLDER_REGEX.findAllMatchIn(sql) + .map(m => m.matched) + .toList + } + + /** + * Replace all placeholders as "?" + * + * @param sql + * @return + */ + private def getPreparedSql(sql: String): String = { + SQL_PLACEHOLDER_REGEX.replaceAllIn(sql, "?") + } + + /** + * Gracefully close DB connection + * + * @param connection + * @param statement + * @param rs + */ + private def closeDbConnection(connection: Connection, statement: Statement): Unit = { + // Close statement + if (statement != null && !statement.isClosed) { + try { + statement.close() + } catch { + case e: Exception => + error("Cannot close PreparedStatement to auth database ", e) + } + } + + // Close connection + if (connection != null && !connection.isClosed) { + try { + connection.close() + } catch { + case e: Exception => + error("Cannot close connection to auth database ", e) + } + } + } +} + From 0e7f0ad04312807d095d6830595bf0ffdaa1d629 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 15:04:08 +0800 Subject: [PATCH 02/30] refactor config and init process.remove unused import. --- .../JdbcAuthenticationProviderImpl.scala | 116 ++++++++++-------- 1 file changed, 68 insertions(+), 48 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 7515a43c308..0e6491487a1 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -18,7 +18,7 @@ package org.apache.kyuubi.service.authentication -import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet, Statement} +import java.sql.{Connection, DriverManager, PreparedStatement, Statement} import javax.security.sasl.AuthenticationException import org.apache.commons.lang3.StringUtils @@ -62,32 +62,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat s" or contains blank space") } - def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" - - debug(configLog("Driver Class", dbDriver)) - debug(configLog("JDBC URL", dbUrl)) - debug(configLog("Database Username", dbUserName)) - debug(configLog("Database Password", dbPassword)) - debug(configLog("Query SQL", querySql)) - - // Check if JDBC parameters valid - if (StringUtils.isBlank(dbDriver) || - StringUtils.isBlank(dbUrl) || - StringUtils.isBlank(dbUserName) || - StringUtils.isBlank(dbPassword)) { - error("User auth Database has not been configured!") - throw new IllegalArgumentException("User auth Database has not been configured!") - } - - // Check Query SQL - if (StringUtils.isBlank(querySql)) { - error("Query SQL not configured!") - throw new IllegalArgumentException("Query SQL not configured!") - } - if (!querySql.toLowerCase().startsWith("select")) { // only allow select query sql - error("Query SQL must start with \"select\"!") - throw new IllegalArgumentException("Query SQL must start with \"select\"!"); - } + checkConfigs // Load Driver Class try { @@ -104,26 +79,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat try { connection = DriverManager.getConnection(dbUrl, dbUserName, dbPassword) - // Replace placeholders by "?" and prepare the statement - queryStatement = connection.prepareStatement(getPreparedSql(querySql)) - - // Extract placeholder list and use its order to pass parameters - val placeholderList: List[String] = getPlaceholderList(querySql) - for (i <- placeholderList.indices) { - val param = placeholderList(i) match { - case USERNAME_SQL_PLACEHOLDER => user - case PASSWORD_SQL_PLACEHOLDER => password - case otherPlaceholder => - error(s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") - throw new IllegalStateException( - s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") - } - - queryStatement.setString(i + 1, param) - } - - // Client side limit 1 - queryStatement.setMaxRows(1) + queryStatement = getAndPrepareStatement(connection, user, password) val resultSet = queryStatement.executeQuery() @@ -146,6 +102,35 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } + private def checkConfigs: Unit = { + def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" + + debug(configLog("Driver Class", dbDriver)) + debug(configLog("JDBC URL", dbUrl)) + debug(configLog("Database Username", dbUserName)) + debug(configLog("Database Password", dbPassword)) + debug(configLog("Query SQL", querySql)) + + // Check if JDBC parameters valid + if (StringUtils.isBlank(dbDriver) || + StringUtils.isBlank(dbUrl) || + StringUtils.isBlank(dbUserName) || + StringUtils.isBlank(dbPassword)) { + error("User auth Database has not been configured!") + throw new IllegalArgumentException("User auth Database has not been configured!") + } + + // Check Query SQL + if (StringUtils.isBlank(querySql)) { + error("Query SQL not configured!") + throw new IllegalArgumentException("Query SQL not configured!") + } + if (!querySql.toLowerCase().startsWith("select")) { // only allow select query sql + error("Query SQL must start with \"select\"!") + throw new IllegalArgumentException("Query SQL must start with \"select\"!"); + } + } + /** * Extract all placeholders from query and put them into a list. * @@ -168,12 +153,47 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat SQL_PLACEHOLDER_REGEX.replaceAllIn(sql, "?") } + /** + * prepare the final query statement + * by replacing placeholder in query sql with user and password + * + * @param connection + * @param user + * @param password + * @return + */ + private def getAndPrepareStatement(connection: Connection, + user: String, + password: String): PreparedStatement = { + // Replace placeholders by "?" and prepare the statement + val stmt = connection.prepareStatement(getPreparedSql(querySql)) + + // Extract placeholder list and use its order to pass parameters + val placeholderList: List[String] = getPlaceholderList(querySql) + for (i <- placeholderList.indices) { + val param = placeholderList(i) match { + case USERNAME_SQL_PLACEHOLDER => user + case PASSWORD_SQL_PLACEHOLDER => password + case otherPlaceholder => + error(s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") + throw new IllegalStateException( + s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") + } + + stmt.setString(i + 1, param) + } + + // Client side limit 1 + stmt.setMaxRows(1) + + stmt + } + /** * Gracefully close DB connection * * @param connection * @param statement - * @param rs */ private def closeDbConnection(connection: Connection, statement: Statement): Unit = { // Close statement From 996f79690fe6d8531970503988c60576b0d8a1d0 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 19:34:15 +0800 Subject: [PATCH 03/30] add unit test in JdbcAuthenticationProviderImplSuite --- kyuubi-common/pom.xml | 6 + .../org/apache/kyuubi/config/KyuubiConf.scala | 8 +- .../JdbcAuthenticationProviderImpl.scala | 8 +- .../JdbcAuthenticationProviderImplSuite.scala | 106 ++++++++++++++++++ 4 files changed, 120 insertions(+), 8 deletions(-) create mode 100644 kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala diff --git a/kyuubi-common/pom.xml b/kyuubi-common/pom.xml index cc0cb9927d0..6109244da3f 100644 --- a/kyuubi-common/pom.xml +++ b/kyuubi-common/pom.xml @@ -137,6 +137,12 @@ failureaccess test + + + org.apache.derby + derby + test + diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala index 88553f1ac3d..7b21dfabae2 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala @@ -645,7 +645,7 @@ object KyuubiConf { .stringConf .createWithDefault("uid") - val AUTHENTICATION_JDBC_PASSWORD_DRIVER: OptionalConfigEntry[String] = + val AUTHENTICATION_JDBC_DRIVER: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.driver.class") .doc("Driver class name for JDBC Password Authentication Provider.") .version("1.6.0") @@ -659,21 +659,21 @@ object KyuubiConf { .stringConf .createOptional - val AUTHENTICATION_JDBC_PASSWORD_USERNAME: OptionalConfigEntry[String] = + val AUTHENTICATION_JDBC_USERNAME: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.username") .doc("Database username for JDBC Password Authentication Provider.") .version("1.6.0") .stringConf .createOptional - val AUTHENTICATION_JDBC_PASSWORD_PASSWORD: OptionalConfigEntry[String] = + val AUTHENTICATION_JDBC_PASSWORD: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.password") .doc("Database password for JDBC Password Authentication Provider.") .version("1.6.0") .stringConf .createOptional - val AUTHENTICATION_JDBC_PASSWORD_QUERY: OptionalConfigEntry[String] = + val AUTHENTICATION_JDBC_QUERY: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.query") .doc("Query SQL template with placeholders " + "for JDBC Password Authentication Provider to execute." + diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 0e6491487a1..f84e8bb09d0 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -30,11 +30,11 @@ import org.apache.kyuubi.config.KyuubiConf._ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticationProvider with Logging { - private val dbDriver = conf.get(AUTHENTICATION_JDBC_PASSWORD_DRIVER).map(s => s.trim).orNull + private val dbDriver = conf.get(AUTHENTICATION_JDBC_DRIVER).map(s => s.trim).orNull private val dbUrl = conf.get(AUTHENTICATION_JDBC_URL).map(s => s.trim).orNull - private val dbUserName = conf.get(AUTHENTICATION_JDBC_PASSWORD_USERNAME).map(s => s.trim).orNull - private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD_PASSWORD).map(s => s.trim).orNull - private val querySql = conf.get(AUTHENTICATION_JDBC_PASSWORD_QUERY).map(s => s.trim).orNull + private val dbUserName = conf.get(AUTHENTICATION_JDBC_USERNAME).map(s => s.trim).orNull + private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD).map(s => s.trim).orNull + private val querySql = conf.get(AUTHENTICATION_JDBC_QUERY).map(s => s.trim).orNull private val SQL_PLACEHOLDER_REGEX = """\$\{.+?}""".r private val USERNAME_SQL_PLACEHOLDER = "${username}" diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala new file mode 100644 index 00000000000..f3403482a1a --- /dev/null +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -0,0 +1,106 @@ +/* + * 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.kyuubi.service.authentication + +import java.sql.{Connection, DriverManager} +import java.util.Properties +import javax.security.sasl.AuthenticationException + +import org.apache.kyuubi.KyuubiFunSuite +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.config.KyuubiConf._ + + +class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { + protected val authUser: String = "liangtiancheng" + protected val authPasswd: String = "liangtiancheng" + + protected val dbUser: String = "liangbowen" + protected val dbPasswd: String = "liangbowen" + + protected var jdbcUrl: String = _ + + private val conf = new KyuubiConf() + var conn: Connection = _ + + override def beforeAll(): Unit = { + // todo start derby database + // todo run ddl、dml for demo user and password + val datasourceProperties = new Properties() + datasourceProperties.put("user", dbUser) + datasourceProperties.put("password", dbPasswd) + + val protocol = "jdbc:derby:" + jdbcUrl = protocol + "derbyAuthDB;create=true" + conn = DriverManager.getConnection(jdbcUrl + + ";user=" + dbUser + + ";password=" + dbPasswd + , datasourceProperties) + + conn.prepareStatement("drop TABLE user_auth ").execute(); + + conn.prepareStatement("CREATE TABLE user_auth (" + + "username CHAR(64) NOT NULL PRIMARY KEY, " + + "passwd_hash CHAR(64))" + ).execute(); + + conn.prepareStatement("truncate TABLE user_auth ").execute(); + + val insertStmt = conn.prepareStatement("INSERT INTO user_auth " + + "(username, passwd_hash) VALUES (?,?)") + insertStmt.setString(1, authUser) + insertStmt.setString(2, authPasswd) + insertStmt.execute(); + + + conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") + conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) + conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) + conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) + conf.set(AUTHENTICATION_JDBC_QUERY, + "select 1 from user_auth " + + "where username=${username} and passwd_hash=${password}") + } + + override def afterAll(): Unit = { + // shutdown derby database + conn.close() + DriverManager.getConnection("jdbc:derby:;shutdown=true") + } + + + test("authenticate tests") { + var providerImpl = new JdbcAuthenticationProviderImpl(conf) + + val e1 = intercept[AuthenticationException](providerImpl.authenticate("", "")) + assert(e1.getMessage.contains("user is null")) + val e2 = intercept[AuthenticationException](providerImpl.authenticate("kyuubi", "")) + assert(e2.getMessage.contains("password is null")) + + providerImpl.authenticate(authUser, authPasswd) + + val e4 = intercept[AuthenticationException]( + providerImpl.authenticate(authPasswd, "pass")) + assert(e4.isInstanceOf[AuthenticationException]) + + conf.unset(AUTHENTICATION_JDBC_URL) + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e5 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e5.getMessage.contains("User auth Database has not been configured!")) + } +} From 49c18c2546830936101311d14dfe00704f075241 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 19:35:51 +0800 Subject: [PATCH 04/30] update --- .../authentication/JdbcAuthenticationProviderImplSuite.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index f3403482a1a..d7620062f08 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -39,8 +39,6 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { var conn: Connection = _ override def beforeAll(): Unit = { - // todo start derby database - // todo run ddl、dml for demo user and password val datasourceProperties = new Properties() datasourceProperties.put("user", dbUser) datasourceProperties.put("password", dbPasswd) From df4be56855ce6f98cdf69d2436472ad61eca0d1a Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 19:42:27 +0800 Subject: [PATCH 05/30] update code style --- .../JdbcAuthenticationProviderImpl.scala | 9 ++++----- .../JdbcAuthenticationProviderImplSuite.scala | 18 ++++++++---------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index f84e8bb09d0..1ce51359421 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -17,7 +17,6 @@ package org.apache.kyuubi.service.authentication - import java.sql.{Connection, DriverManager, PreparedStatement, Statement} import javax.security.sasl.AuthenticationException @@ -162,9 +161,10 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @param password * @return */ - private def getAndPrepareStatement(connection: Connection, - user: String, - password: String): PreparedStatement = { + private def getAndPrepareStatement( + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement val stmt = connection.prepareStatement(getPreparedSql(querySql)) @@ -217,4 +217,3 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } } - diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index d7620062f08..c4cace10d9d 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -25,7 +25,6 @@ import org.apache.kyuubi.KyuubiFunSuite import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ - class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { protected val authUser: String = "liangtiancheng" protected val authPasswd: String = "liangtiancheng" @@ -45,17 +44,17 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { val protocol = "jdbc:derby:" jdbcUrl = protocol + "derbyAuthDB;create=true" - conn = DriverManager.getConnection(jdbcUrl - + ";user=" + dbUser - + ";password=" + dbPasswd - , datasourceProperties) + conn = DriverManager.getConnection( + jdbcUrl + + ";user=" + dbUser + + ";password=" + dbPasswd, + datasourceProperties) conn.prepareStatement("drop TABLE user_auth ").execute(); conn.prepareStatement("CREATE TABLE user_auth (" + "username CHAR(64) NOT NULL PRIMARY KEY, " + - "passwd_hash CHAR(64))" - ).execute(); + "passwd_hash CHAR(64))").execute(); conn.prepareStatement("truncate TABLE user_auth ").execute(); @@ -65,12 +64,12 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { insertStmt.setString(2, authPasswd) insertStmt.execute(); - conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) - conf.set(AUTHENTICATION_JDBC_QUERY, + conf.set( + AUTHENTICATION_JDBC_QUERY, "select 1 from user_auth " + "where username=${username} and passwd_hash=${password}") } @@ -81,7 +80,6 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { DriverManager.getConnection("jdbc:derby:;shutdown=true") } - test("authenticate tests") { var providerImpl = new JdbcAuthenticationProviderImpl(conf) From 7025330b1d3d1cc1e26344e05a6550068dc85f42 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 21:35:17 +0800 Subject: [PATCH 06/30] fix derby startup error in test --- .../JdbcAuthenticationProviderImplSuite.scala | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index c4cace10d9d..3ccd9336a64 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -17,14 +17,15 @@ package org.apache.kyuubi.service.authentication +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.config.KyuubiConf._ +import org.apache.kyuubi.{KyuubiFunSuite, Utils} + +import java.nio.file.Path import java.sql.{Connection, DriverManager} import java.util.Properties import javax.security.sasl.AuthenticationException -import org.apache.kyuubi.KyuubiFunSuite -import org.apache.kyuubi.config.KyuubiConf -import org.apache.kyuubi.config.KyuubiConf._ - class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { protected val authUser: String = "liangtiancheng" protected val authPasswd: String = "liangtiancheng" @@ -36,21 +37,24 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { private val conf = new KyuubiConf() var conn: Connection = _ + var authDb: Path = _ override def beforeAll(): Unit = { val datasourceProperties = new Properties() datasourceProperties.put("user", dbUser) datasourceProperties.put("password", dbPasswd) - val protocol = "jdbc:derby:" - jdbcUrl = protocol + "derbyAuthDB;create=true" + authDb = Utils.createTempDir(namePrefix = getClass.getSimpleName) + authDb.toFile.delete() + + jdbcUrl = s"jdbc:derby:;databaseName=$authDb;create=true" conn = DriverManager.getConnection( jdbcUrl + ";user=" + dbUser + ";password=" + dbPasswd, datasourceProperties) - conn.prepareStatement("drop TABLE user_auth ").execute(); + conn.prepareStatement("create schema " + dbUser).execute(); conn.prepareStatement("CREATE TABLE user_auth (" + "username CHAR(64) NOT NULL PRIMARY KEY, " + @@ -76,8 +80,11 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { override def afterAll(): Unit = { // shutdown derby database - conn.close() - DriverManager.getConnection("jdbc:derby:;shutdown=true") + try { + DriverManager.getConnection(s"jdbc:derby:;databaseName=$authDb;shutdown=true") + } catch { + case e: Throwable => + } } test("authenticate tests") { From 46cc1dda1a90805f7ea5cda8ffe33ae9dc7b88cb Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 21:46:21 +0800 Subject: [PATCH 07/30] add config docs in docs/deployment/settings.md --- docs/deployment/settings.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md index 13c7e8ac0a6..6ba0f541ec1 100644 --- a/docs/deployment/settings.md +++ b/docs/deployment/settings.md @@ -142,6 +142,10 @@ kyuubi.authentication.ldap.base.dn|<undefined>|LDAP base DN.|string|1.0.0 kyuubi.authentication.ldap.domain|<undefined>|LDAP domain.|string|1.0.0 kyuubi.authentication.ldap.guidKey|uid|LDAP attribute name whose values are unique in this LDAP server.For example:uid or cn.|string|1.2.0 kyuubi.authentication.ldap.url|<undefined>|SPACE character separated LDAP connection URL(s).|string|1.0.0 +kyuubi.authentication.jdbc.url|<undefined>|JDBC URL to database for authentication.|string|1.6.0 +kyuubi.authentication.jdbc.user|<undefined>|JDBC user to databse for authentication.|string|1.6.0 +kyuubi.authentication.jdbc.password|<undefined>|JDBC password to database for authentication|string|1.6.0 +kyuubi.authentication.jdbc.query|<undefined>|JDBC query sql to database for authentication. Query SQL template with placeholders for JDBC Password Authentication Provider to execute. Available placeholders are:
  • `${username}`
  • `${password}`
Don't quote the placeholders, query will be processed as prepared statement, while username and password will be passed as parameters. For example: `SELECT 1 FROM auth_table WHERE user=${username} AND passwd=MD5(CONCAT(salt,${password}));` Will be prepared as: `SELECT 1 FROM auth_table WHERE user=? AND passwd=MD5(CONCAT(salt,?));` |string|1.6.0 kyuubi.authentication.sasl.qop|auth|Sasl QOP enable higher levels of protection for Kyuubi communication with clients.
  • auth - authentication only (default)
  • auth-int - authentication plus integrity protection
  • auth-conf - authentication plus integrity and confidentiality protection. This is applicable only if Kyuubi is configured to use Kerberos authentication.
|string|1.0.0 From 15176b2b10cdc41fadb236f463de8c94d1f2dcaa Mon Sep 17 00:00:00 2001 From: liangbowen Date: Mon, 15 Aug 2022 21:49:56 +0800 Subject: [PATCH 08/30] fix import orders --- .../JdbcAuthenticationProviderImplSuite.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index 3ccd9336a64..c036b293006 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -17,15 +17,15 @@ package org.apache.kyuubi.service.authentication -import org.apache.kyuubi.config.KyuubiConf -import org.apache.kyuubi.config.KyuubiConf._ -import org.apache.kyuubi.{KyuubiFunSuite, Utils} - import java.nio.file.Path import java.sql.{Connection, DriverManager} import java.util.Properties import javax.security.sasl.AuthenticationException +import org.apache.kyuubi.{KyuubiFunSuite, Utils} +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.config.KyuubiConf._ + class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { protected val authUser: String = "liangtiancheng" protected val authPasswd: String = "liangtiancheng" From cd2c7c2b4231500bfe8cd21574d35040a3ff4460 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 00:00:53 +0800 Subject: [PATCH 09/30] update settings.md config doc --- docs/deployment/settings.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md index 6ba0f541ec1..d93e9231e59 100644 --- a/docs/deployment/settings.md +++ b/docs/deployment/settings.md @@ -138,14 +138,15 @@ Key | Default | Meaning | Type | Since --- | --- | --- | --- | --- kyuubi.authentication|NONE|A comma separated list of client authentication types.
  • NOSASL: raw transport.
  • NONE: no authentication check.
  • KERBEROS: Kerberos/GSSAPI authentication.
  • CUSTOM: User-defined authentication.
  • LDAP: Lightweight Directory Access Protocol authentication.
Note that: For KERBEROS, it is SASL/GSSAPI mechanism, and for NONE, CUSTOM and LDAP, they are all SASL/PLAIN mechanism. If only NOSASL is specified, the authentication will be NOSASL. For SASL authentication, KERBEROS and PLAIN auth type are supported at the same time, and only the first specified PLAIN auth type is valid.|seq|1.0.0 kyuubi.authentication.custom.class|<undefined>|User-defined authentication implementation of org.apache.kyuubi.service.authentication.PasswdAuthenticationProvider|string|1.3.0 +kyuubi.authentication.jdbc.driver.class|<undefined>|Driver class name for JDBC Password Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.password|<undefined>|Database password for JDBC Password Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.query|<undefined>|Query SQL template with placeholders for JDBC Password Authentication Provider to execute.Available placeholders are:
  • ${username}
  • ${password}
Don't quote the placeholders, query will be processed as prepared statement, while username and password will be passed as parameters.For example: SELECT 1 FROM auth_table WHERE user=${username} AND passwd=MD5(CONCAT(salt,${password})); Will be prepared as: SELECT 1 FROM auth_table WHERE user=? AND passwd=MD5(CONCAT(salt,?)); |string|1.6.0 +kyuubi.authentication.jdbc.url|<undefined>|JDBC URL for JDBC Password Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.username|<undefined>|Database username for JDBC Password Authentication Provider.|string|1.6.0 kyuubi.authentication.ldap.base.dn|<undefined>|LDAP base DN.|string|1.0.0 kyuubi.authentication.ldap.domain|<undefined>|LDAP domain.|string|1.0.0 kyuubi.authentication.ldap.guidKey|uid|LDAP attribute name whose values are unique in this LDAP server.For example:uid or cn.|string|1.2.0 kyuubi.authentication.ldap.url|<undefined>|SPACE character separated LDAP connection URL(s).|string|1.0.0 -kyuubi.authentication.jdbc.url|<undefined>|JDBC URL to database for authentication.|string|1.6.0 -kyuubi.authentication.jdbc.user|<undefined>|JDBC user to databse for authentication.|string|1.6.0 -kyuubi.authentication.jdbc.password|<undefined>|JDBC password to database for authentication|string|1.6.0 -kyuubi.authentication.jdbc.query|<undefined>|JDBC query sql to database for authentication. Query SQL template with placeholders for JDBC Password Authentication Provider to execute. Available placeholders are:
  • `${username}`
  • `${password}`
Don't quote the placeholders, query will be processed as prepared statement, while username and password will be passed as parameters. For example: `SELECT 1 FROM auth_table WHERE user=${username} AND passwd=MD5(CONCAT(salt,${password}));` Will be prepared as: `SELECT 1 FROM auth_table WHERE user=? AND passwd=MD5(CONCAT(salt,?));` |string|1.6.0 kyuubi.authentication.sasl.qop|auth|Sasl QOP enable higher levels of protection for Kyuubi communication with clients.
  • auth - authentication only (default)
  • auth-int - authentication plus integrity protection
  • auth-conf - authentication plus integrity and confidentiality protection. This is applicable only if Kyuubi is configured to use Kerberos authentication.
|string|1.0.0 From 1dc4187b671c696163935fa3892de8ddafcd46e3 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 00:01:07 +0800 Subject: [PATCH 10/30] update settings.md config doc --- .../org/apache/kyuubi/config/KyuubiConf.scala | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala index 7b21dfabae2..6b7596e7ae5 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/config/KyuubiConf.scala @@ -593,7 +593,9 @@ object KyuubiConf { "
  • NONE: no authentication check.
  • " + "
  • KERBEROS: Kerberos/GSSAPI authentication.
  • " + "
  • CUSTOM: User-defined authentication.
  • " + - "
  • LDAP: Lightweight Directory Access Protocol authentication.
  • " + + "
  • JDBC: JDBC query authentication.
  • " + + "
  • LDAP: Lightweight Directory Access Protocol authentication.
  • " + + "" + " Note that: For KERBEROS, it is SASL/GSSAPI mechanism," + " and for NONE, CUSTOM and LDAP, they are all SASL/PLAIN mechanism." + " If only NOSASL is specified, the authentication will be NOSASL." + @@ -647,28 +649,28 @@ object KyuubiConf { val AUTHENTICATION_JDBC_DRIVER: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.driver.class") - .doc("Driver class name for JDBC Password Authentication Provider.") + .doc("Driver class name for JDBC Authentication Provider.") .version("1.6.0") .stringConf .createOptional val AUTHENTICATION_JDBC_URL: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.url") - .doc("JDBC URL for JDBC Password Authentication Provider.") + .doc("JDBC URL for JDBC Authentication Provider.") .version("1.6.0") .stringConf .createOptional val AUTHENTICATION_JDBC_USERNAME: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.username") - .doc("Database username for JDBC Password Authentication Provider.") + .doc("Database username for JDBC Authentication Provider.") .version("1.6.0") .stringConf .createOptional val AUTHENTICATION_JDBC_PASSWORD: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.password") - .doc("Database password for JDBC Password Authentication Provider.") + .doc("Database password for JDBC Authentication Provider.") .version("1.6.0") .stringConf .createOptional @@ -676,16 +678,16 @@ object KyuubiConf { val AUTHENTICATION_JDBC_QUERY: OptionalConfigEntry[String] = buildConf("kyuubi.authentication.jdbc.query") .doc("Query SQL template with placeholders " + - "for JDBC Password Authentication Provider to execute." + + "for JDBC Authentication Provider to execute. " + + "Authentication passes if at least one row fetched in the result set." + "Available placeholders are:
      " + - "
    • ${username}
    • " + - "
    • ${password}
    " + - "Don't quote the placeholders, query will be processed as prepared statement, " + - "while username and password will be passed as parameters." + - "For example: SELECT 1 FROM auth_table WHERE user=${username} AND " + - "passwd=MD5(CONCAT(salt,${password})); " + - "Will be prepared as: SELECT 1 FROM auth_table " + - "WHERE user=? AND passwd=MD5(CONCAT(salt,?)); ") + "
  • `${username}`
  • " + + "
  • `${password}`
  • " + + "eg.: query sql `SELECT 1 FROM auth_table WHERE user=${username} AND " + + "passwd=MD5(CONCAT(salt,${password}));` " + + "will be prepared as: `SELECT 1 FROM auth_table " + + "WHERE user=? AND passwd=MD5(CONCAT(salt,?));`" + + " with value replacement of `username` and `password` in string type.") .version("1.6.0") .stringConf .createOptional From 575301ca2e483d5882e93d47d190b992a4359986 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 00:02:29 +0800 Subject: [PATCH 11/30] update options usage --- .../JdbcAuthenticationProviderImpl.scala | 43 +++++++++---------- .../JdbcAuthenticationProviderImplSuite.scala | 4 +- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 1ce51359421..e24ea888862 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -29,11 +29,11 @@ import org.apache.kyuubi.config.KyuubiConf._ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticationProvider with Logging { - private val dbDriver = conf.get(AUTHENTICATION_JDBC_DRIVER).map(s => s.trim).orNull - private val dbUrl = conf.get(AUTHENTICATION_JDBC_URL).map(s => s.trim).orNull - private val dbUserName = conf.get(AUTHENTICATION_JDBC_USERNAME).map(s => s.trim).orNull - private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD).map(s => s.trim).orNull - private val querySql = conf.get(AUTHENTICATION_JDBC_QUERY).map(s => s.trim).orNull + private val dbDriver = conf.get(AUTHENTICATION_JDBC_DRIVER) + private val dbUrl = conf.get(AUTHENTICATION_JDBC_URL) + private val dbUserName = conf.get(AUTHENTICATION_JDBC_USERNAME) + private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD) + private val querySql = conf.get(AUTHENTICATION_JDBC_QUERY) private val SQL_PLACEHOLDER_REGEX = """\$\{.+?}""".r private val USERNAME_SQL_PLACEHOLDER = "${username}" @@ -65,7 +65,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat // Load Driver Class try { - Class.forName(dbDriver) + Class.forName(dbDriver.get) } catch { case e: ClassNotFoundException => error(s"Driver class not found: $dbDriver") @@ -76,7 +76,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat var queryStatement: PreparedStatement = null try { - connection = DriverManager.getConnection(dbUrl, dbUserName, dbPassword) + connection = DriverManager.getConnection(dbUrl.get, dbUserName.orNull, dbPassword.orNull) queryStatement = getAndPrepareStatement(connection, user, password) @@ -104,27 +104,24 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat private def checkConfigs: Unit = { def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" - debug(configLog("Driver Class", dbDriver)) - debug(configLog("JDBC URL", dbUrl)) - debug(configLog("Database Username", dbUserName)) - debug(configLog("Database Password", dbPassword)) - debug(configLog("Query SQL", querySql)) + debug(configLog("Driver Class", dbDriver.orNull)) + debug(configLog("JDBC URL", dbUrl.orNull)) + debug(configLog("Database Username", dbUserName.orNull)) + debug(configLog("Database Password", dbPassword.orNull)) + debug(configLog("Query SQL", querySql.orNull)) // Check if JDBC parameters valid - if (StringUtils.isBlank(dbDriver) || - StringUtils.isBlank(dbUrl) || - StringUtils.isBlank(dbUserName) || - StringUtils.isBlank(dbPassword)) { + if (dbDriver.isEmpty || dbUrl.isEmpty || dbUserName.isEmpty || dbPassword.isEmpty) { error("User auth Database has not been configured!") throw new IllegalArgumentException("User auth Database has not been configured!") } // Check Query SQL - if (StringUtils.isBlank(querySql)) { + if (querySql.isEmpty) { error("Query SQL not configured!") throw new IllegalArgumentException("Query SQL not configured!") } - if (!querySql.toLowerCase().startsWith("select")) { // only allow select query sql + if (!querySql.get.trim.toLowerCase.startsWith("select")) { // only allow select query sql error("Query SQL must start with \"select\"!") throw new IllegalArgumentException("Query SQL must start with \"select\"!"); } @@ -162,14 +159,14 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @return */ private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement - val stmt = connection.prepareStatement(getPreparedSql(querySql)) + val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) // Extract placeholder list and use its order to pass parameters - val placeholderList: List[String] = getPlaceholderList(querySql) + val placeholderList: List[String] = getPlaceholderList(querySql.get) for (i <- placeholderList.indices) { val param = placeholderList(i) match { case USERNAME_SQL_PLACEHOLDER => user diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index c036b293006..fe8a5174359 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -63,7 +63,7 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { conn.prepareStatement("truncate TABLE user_auth ").execute(); val insertStmt = conn.prepareStatement("INSERT INTO user_auth " + - "(username, passwd_hash) VALUES (?,?)") + "(username, passwd) VALUES (?,?)") insertStmt.setString(1, authUser) insertStmt.setString(2, authPasswd) insertStmt.execute(); @@ -75,7 +75,7 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { conf.set( AUTHENTICATION_JDBC_QUERY, "select 1 from user_auth " + - "where username=${username} and passwd_hash=${password}") + "where username=${username} and passwd=${password}") } override def afterAll(): Unit = { From 30974d16ca6d7e67710fdef968fdc50caca4df25 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 00:04:14 +0800 Subject: [PATCH 12/30] update format --- .../authentication/JdbcAuthenticationProviderImpl.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index e24ea888862..e2156a2513b 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -159,9 +159,9 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @return */ private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) From 36729197cf1374f55d55c82a837b7eb6ff5f3392 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 08:23:13 +0800 Subject: [PATCH 13/30] fix ddl statement and remove truncate statement in test --- .../JdbcAuthenticationProviderImplSuite.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index fe8a5174359..1b5f9aed4de 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -57,10 +57,8 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { conn.prepareStatement("create schema " + dbUser).execute(); conn.prepareStatement("CREATE TABLE user_auth (" + - "username CHAR(64) NOT NULL PRIMARY KEY, " + - "passwd_hash CHAR(64))").execute(); - - conn.prepareStatement("truncate TABLE user_auth ").execute(); + "username VARCHAR(64) NOT NULL PRIMARY KEY, " + + "passwd VARCHAR(64))").execute(); val insertStmt = conn.prepareStatement("INSERT INTO user_auth " + "(username, passwd) VALUES (?,?)") From cdec2066fed7de0994d63f3f0264ff17c0982e2d Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 09:01:54 +0800 Subject: [PATCH 14/30] more test cases --- .../JdbcAuthenticationProviderImpl.scala | 25 +++--- .../JdbcAuthenticationProviderImplSuite.scala | 76 ++++++++++++++----- 2 files changed, 73 insertions(+), 28 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index e2156a2513b..7a75a63d341 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -111,19 +111,24 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat debug(configLog("Query SQL", querySql.orNull)) // Check if JDBC parameters valid - if (dbDriver.isEmpty || dbUrl.isEmpty || dbUserName.isEmpty || dbPassword.isEmpty) { - error("User auth Database has not been configured!") - throw new IllegalArgumentException("User auth Database has not been configured!") + if (dbDriver.isEmpty) { + throw new IllegalArgumentException("JDBC driver class is not configured.") + } + + if (dbUrl.isEmpty) { + throw new IllegalArgumentException("JDBC url is not configured") + } + + if (dbUserName.isEmpty || dbPassword.isEmpty) { + throw new IllegalArgumentException("JDBC username or password is not configured") } // Check Query SQL if (querySql.isEmpty) { - error("Query SQL not configured!") - throw new IllegalArgumentException("Query SQL not configured!") + throw new IllegalArgumentException("Query SQL is not configured") } if (!querySql.get.trim.toLowerCase.startsWith("select")) { // only allow select query sql - error("Query SQL must start with \"select\"!") - throw new IllegalArgumentException("Query SQL must start with \"select\"!"); + throw new IllegalArgumentException("Query SQL must start with \"SELECT\""); } } @@ -159,9 +164,9 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @return */ private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index 1b5f9aed4de..e7ebc00575c 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -27,19 +27,20 @@ import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { + protected val dbUser: String = "bowenliang123" + protected val dbPasswd: String = "bowenliang123" + protected var jdbcUrl: String = _ + protected val authUser: String = "liangtiancheng" protected val authPasswd: String = "liangtiancheng" - protected val dbUser: String = "liangbowen" - protected val dbPasswd: String = "liangbowen" - - protected var jdbcUrl: String = _ - - private val conf = new KyuubiConf() + protected var conf = new KyuubiConf() var conn: Connection = _ var authDb: Path = _ override def beforeAll(): Unit = { + super.beforeAll() + val datasourceProperties = new Properties() datasourceProperties.put("user", dbUser) datasourceProperties.put("password", dbPasswd) @@ -54,7 +55,7 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { + ";password=" + dbPasswd, datasourceProperties) - conn.prepareStatement("create schema " + dbUser).execute(); + conn.prepareStatement("CREATE SCHEMA " + dbUser).execute(); conn.prepareStatement("CREATE TABLE user_auth (" + "username VARCHAR(64) NOT NULL PRIMARY KEY, " + @@ -66,17 +67,12 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { insertStmt.setString(2, authPasswd) insertStmt.execute(); - conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") - conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) - conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) - conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) - conf.set( - AUTHENTICATION_JDBC_QUERY, - "select 1 from user_auth " + - "where username=${username} and passwd=${password}") + conf = genJdbcAuthConfigs } override def afterAll(): Unit = { + super.afterAll() + // shutdown derby database try { DriverManager.getConnection(s"jdbc:derby:;databaseName=$authDb;shutdown=true") @@ -85,23 +81,67 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { } } + def genJdbcAuthConfigs: KyuubiConf = { + conf = new KyuubiConf() + conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") + conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) + conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) + conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) + conf.set( + AUTHENTICATION_JDBC_QUERY, + "SELECT 1 FROM user_auth " + + " WHERE username=${username} and passwd=${password}") + conf + } + test("authenticate tests") { var providerImpl = new JdbcAuthenticationProviderImpl(conf) + providerImpl.authenticate(authUser, authPasswd) + val e1 = intercept[AuthenticationException](providerImpl.authenticate("", "")) assert(e1.getMessage.contains("user is null")) + val e2 = intercept[AuthenticationException](providerImpl.authenticate("kyuubi", "")) assert(e2.getMessage.contains("password is null")) - providerImpl.authenticate(authUser, authPasswd) - val e4 = intercept[AuthenticationException]( providerImpl.authenticate(authPasswd, "pass")) assert(e4.isInstanceOf[AuthenticationException]) + conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_URL) providerImpl = new JdbcAuthenticationProviderImpl(conf) val e5 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) - assert(e5.getMessage.contains("User auth Database has not been configured!")) + assert(e5.getMessage.contains("JDBC url is not configured")) + + conf = genJdbcAuthConfigs + conf.unset(AUTHENTICATION_JDBC_USERNAME) + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e6 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e6.getMessage.contains("JDBC username or password is not configured")) + + conf = genJdbcAuthConfigs + conf.unset(AUTHENTICATION_JDBC_PASSWORD) + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e7 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e7.getMessage.contains("JDBC username or password is not configured")) + + conf = genJdbcAuthConfigs + conf.unset(AUTHENTICATION_JDBC_QUERY) + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e8 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e8.getMessage.contains("Query SQL is not configured")) + + conf.set(AUTHENTICATION_JDBC_QUERY, "INSERT INTO user_auth (username, password) " + + " VALUES ('demouser','demopassword'); ") + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e9 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e9.getMessage.contains("Query SQL must start with \"SELECT\"")) + + conf.unset(AUTHENTICATION_JDBC_URL) + providerImpl = new JdbcAuthenticationProviderImpl(conf) + val e10 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + assert(e10.getMessage.contains("JDBC url is not configured")) } } From 653bc1261318f56edcd42dc09d8643a56ca277f6 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 09:27:53 +0800 Subject: [PATCH 15/30] add more checks for query sql --- .../JdbcAuthenticationProviderImpl.scala | 35 ++++++++++++------- .../JdbcAuthenticationProviderImplSuite.scala | 32 +++++++++-------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 7a75a63d341..e06bd49e293 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -63,14 +63,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat checkConfigs - // Load Driver Class - try { - Class.forName(dbDriver.get) - } catch { - case e: ClassNotFoundException => - error(s"Driver class not found: $dbDriver") - throw e; - } + loadJdbcDriverClass var connection: Connection = null var queryStatement: PreparedStatement = null @@ -127,9 +120,27 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat if (querySql.isEmpty) { throw new IllegalArgumentException("Query SQL is not configured") } - if (!querySql.get.trim.toLowerCase.startsWith("select")) { // only allow select query sql + val querySqlInLowerCase = querySql.get.trim.toLowerCase + if (!querySqlInLowerCase.startsWith("select")) { // allow select query sql only throw new IllegalArgumentException("Query SQL must start with \"SELECT\""); } + if (!querySqlInLowerCase.contains("where")) { + warn("Query SQL does not contains \"WHERE\" keyword"); + } + if (!querySqlInLowerCase.contains("${username}")) { + warn("Query SQL does not contains \"${username}\" placeholder"); + } + } + + private def loadJdbcDriverClass: Unit = { + // Load Driver Class + try { + Class.forName(dbDriver.get) + } catch { + case e: ClassNotFoundException => + error(s"JDBC Driver class not found: $dbDriver") + throw e; + } } /** @@ -164,9 +175,9 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @return */ private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index e7ebc00575c..afae8443586 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -81,19 +81,6 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { } } - def genJdbcAuthConfigs: KyuubiConf = { - conf = new KyuubiConf() - conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") - conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) - conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) - conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) - conf.set( - AUTHENTICATION_JDBC_QUERY, - "SELECT 1 FROM user_auth " + - " WHERE username=${username} and passwd=${password}") - conf - } - test("authenticate tests") { var providerImpl = new JdbcAuthenticationProviderImpl(conf) @@ -133,8 +120,10 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { val e8 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) assert(e8.getMessage.contains("Query SQL is not configured")) - conf.set(AUTHENTICATION_JDBC_QUERY, "INSERT INTO user_auth (username, password) " + - " VALUES ('demouser','demopassword'); ") + conf.set( + AUTHENTICATION_JDBC_QUERY, + "INSERT INTO user_auth (username, password) " + + " VALUES ('demouser','demopassword'); ") providerImpl = new JdbcAuthenticationProviderImpl(conf) val e9 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) assert(e9.getMessage.contains("Query SQL must start with \"SELECT\"")) @@ -144,4 +133,17 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { val e10 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) assert(e10.getMessage.contains("JDBC url is not configured")) } + + private def genJdbcAuthConfigs: KyuubiConf = { + conf = new KyuubiConf() + conf.set(AUTHENTICATION_JDBC_DRIVER, "org.apache.derby.jdbc.AutoloadedDriver") + conf.set(AUTHENTICATION_JDBC_URL, jdbcUrl) + conf.set(AUTHENTICATION_JDBC_USERNAME, dbUser) + conf.set(AUTHENTICATION_JDBC_PASSWORD, dbPasswd) + conf.set( + AUTHENTICATION_JDBC_QUERY, + "SELECT 1 FROM user_auth " + + " WHERE username=${username} and passwd=${password}") + conf + } } From aeb19ce5869b8f33bff5cc80241072d38725c488 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 13:20:44 +0800 Subject: [PATCH 16/30] update doc --- docs/deployment/settings.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md index d93e9231e59..a1d168ccc17 100644 --- a/docs/deployment/settings.md +++ b/docs/deployment/settings.md @@ -136,13 +136,13 @@ You can configure the Kyuubi properties in `$KYUUBI_HOME/conf/kyuubi-defaults.co Key | Default | Meaning | Type | Since --- | --- | --- | --- | --- -kyuubi.authentication|NONE|A comma separated list of client authentication types.
    • NOSASL: raw transport.
    • NONE: no authentication check.
    • KERBEROS: Kerberos/GSSAPI authentication.
    • CUSTOM: User-defined authentication.
    • LDAP: Lightweight Directory Access Protocol authentication.
    Note that: For KERBEROS, it is SASL/GSSAPI mechanism, and for NONE, CUSTOM and LDAP, they are all SASL/PLAIN mechanism. If only NOSASL is specified, the authentication will be NOSASL. For SASL authentication, KERBEROS and PLAIN auth type are supported at the same time, and only the first specified PLAIN auth type is valid.|seq|1.0.0 +kyuubi.authentication|NONE|A comma separated list of client authentication types.
    • NOSASL: raw transport.
    • NONE: no authentication check.
    • KERBEROS: Kerberos/GSSAPI authentication.
    • CUSTOM: User-defined authentication.
    • JDBC: JDBC query authentication.
    • LDAP: Lightweight Directory Access Protocol authentication.
    Note that: For KERBEROS, it is SASL/GSSAPI mechanism, and for NONE, CUSTOM and LDAP, they are all SASL/PLAIN mechanism. If only NOSASL is specified, the authentication will be NOSASL. For SASL authentication, KERBEROS and PLAIN auth type are supported at the same time, and only the first specified PLAIN auth type is valid.|seq|1.0.0 kyuubi.authentication.custom.class|<undefined>|User-defined authentication implementation of org.apache.kyuubi.service.authentication.PasswdAuthenticationProvider|string|1.3.0 -kyuubi.authentication.jdbc.driver.class|<undefined>|Driver class name for JDBC Password Authentication Provider.|string|1.6.0 -kyuubi.authentication.jdbc.password|<undefined>|Database password for JDBC Password Authentication Provider.|string|1.6.0 -kyuubi.authentication.jdbc.query|<undefined>|Query SQL template with placeholders for JDBC Password Authentication Provider to execute.Available placeholders are:
    • ${username}
    • ${password}
    Don't quote the placeholders, query will be processed as prepared statement, while username and password will be passed as parameters.For example: SELECT 1 FROM auth_table WHERE user=${username} AND passwd=MD5(CONCAT(salt,${password})); Will be prepared as: SELECT 1 FROM auth_table WHERE user=? AND passwd=MD5(CONCAT(salt,?)); |string|1.6.0 -kyuubi.authentication.jdbc.url|<undefined>|JDBC URL for JDBC Password Authentication Provider.|string|1.6.0 -kyuubi.authentication.jdbc.username|<undefined>|Database username for JDBC Password Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.driver.class|<undefined>|Driver class name for JDBC Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.password|<undefined>|Database password for JDBC Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.query|<undefined>|Query SQL template with placeholders for JDBC Authentication Provider to execute. Authentication passes if at least one row fetched in the result set.Available placeholders are:
    • `${username}`
    • `${password}`
    eg.: query sql `SELECT 1 FROM auth_table WHERE user=${username} AND passwd=MD5(CONCAT(salt,${password}));` will be prepared as: `SELECT 1 FROM auth_table WHERE user=? AND passwd=MD5(CONCAT(salt,?));` with value replacement of `username` and `password` in string type.|string|1.6.0 +kyuubi.authentication.jdbc.url|<undefined>|JDBC URL for JDBC Authentication Provider.|string|1.6.0 +kyuubi.authentication.jdbc.username|<undefined>|Database username for JDBC Authentication Provider.|string|1.6.0 kyuubi.authentication.ldap.base.dn|<undefined>|LDAP base DN.|string|1.0.0 kyuubi.authentication.ldap.domain|<undefined>|LDAP domain.|string|1.0.0 kyuubi.authentication.ldap.guidKey|uid|LDAP attribute name whose values are unique in this LDAP server.For example:uid or cn.|string|1.2.0 From 9885f8136555bc9182ee6128457a569775c4de5a Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 14:38:59 +0800 Subject: [PATCH 17/30] add JDBC condition for getValidPasswordAuthMethod --- .../service/authentication/KyuubiAuthenticationFactory.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala index fa019ebde7c..ded32371fd9 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala @@ -20,7 +20,6 @@ package org.apache.kyuubi.service.authentication import java.io.IOException import javax.security.auth.login.LoginException import javax.security.sasl.Sasl - import org.apache.hadoop.conf.Configuration import org.apache.hadoop.security.UserGroupInformation import org.apache.hadoop.security.authentication.util.KerberosName @@ -28,11 +27,10 @@ import org.apache.hadoop.security.authorize.ProxyUsers import org.apache.hive.service.rpc.thrift.TCLIService.Iface import org.apache.thrift.TProcessorFactory import org.apache.thrift.transport.{TSaslServerTransport, TTransportException, TTransportFactory} - import org.apache.kyuubi.{KyuubiSQLException, Logging} import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ -import org.apache.kyuubi.service.authentication.AuthMethods.AuthMethod +import org.apache.kyuubi.service.authentication.AuthMethods.{AuthMethod, JDBC} import org.apache.kyuubi.service.authentication.AuthTypes._ class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer: Boolean = true) extends Logging { @@ -140,6 +138,7 @@ class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer: Boolean = true) ex debug(authTypes) if (none) AuthMethods.NONE else if (authTypes.contains(LDAP)) AuthMethods.LDAP + else if (authTypes.contains(JDBC)) AuthMethods.JDBC else if (authTypes.contains(CUSTOM)) AuthMethods.CUSTOM else throw new IllegalArgumentException("No valid Password Auth detected") } From 4ebe12e219fc0269ac29ef4b7227ab84df284a3c Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 14:44:21 +0800 Subject: [PATCH 18/30] add JDBC value to AuthTypes enum --- .../org/apache/kyuubi/service/authentication/AuthTypes.scala | 2 +- .../service/authentication/KyuubiAuthenticationFactory.scala | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthTypes.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthTypes.scala index eacebad31ca..8bb1f6c4971 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthTypes.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/AuthTypes.scala @@ -20,5 +20,5 @@ package org.apache.kyuubi.service.authentication object AuthTypes extends Enumeration { type AuthType = Value - val NOSASL, NONE, LDAP, KERBEROS, CUSTOM = Value + val NOSASL, NONE, LDAP, JDBC, KERBEROS, CUSTOM = Value } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala index ded32371fd9..5f429fa4ed7 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactory.scala @@ -20,6 +20,7 @@ package org.apache.kyuubi.service.authentication import java.io.IOException import javax.security.auth.login.LoginException import javax.security.sasl.Sasl + import org.apache.hadoop.conf.Configuration import org.apache.hadoop.security.UserGroupInformation import org.apache.hadoop.security.authentication.util.KerberosName @@ -27,10 +28,11 @@ import org.apache.hadoop.security.authorize.ProxyUsers import org.apache.hive.service.rpc.thrift.TCLIService.Iface import org.apache.thrift.TProcessorFactory import org.apache.thrift.transport.{TSaslServerTransport, TTransportException, TTransportFactory} + import org.apache.kyuubi.{KyuubiSQLException, Logging} import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ -import org.apache.kyuubi.service.authentication.AuthMethods.{AuthMethod, JDBC} +import org.apache.kyuubi.service.authentication.AuthMethods.AuthMethod import org.apache.kyuubi.service.authentication.AuthTypes._ class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer: Boolean = true) extends Logging { From 1c956dfb5cdbb8f0ebaeeabde3cecd17bf96488f Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 15:08:35 +0800 Subject: [PATCH 19/30] update KyuubiAuthenticationFactorySuite --- .../authentication/KyuubiAuthenticationFactorySuite.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala index bfcd3011e9b..19b89b47e41 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/KyuubiAuthenticationFactorySuite.scala @@ -59,7 +59,7 @@ class KyuubiAuthenticationFactorySuite extends KyuubiFunSuite { val conf = KyuubiConf().set(KyuubiConf.AUTHENTICATION_METHOD, Seq("INVALID")) val e = intercept[IllegalArgumentException](new KyuubiAuthenticationFactory(conf)) assert(e.getMessage contains "the authentication type should be one or more of" + - " NOSASL,NONE,LDAP,KERBEROS,CUSTOM") + " NOSASL,NONE,LDAP,JDBC,KERBEROS,CUSTOM") } test("AuthType LDAP") { From 5a0ac49598fbc0371b9922b2e3f0d3d792fa5e58 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 15:43:51 +0800 Subject: [PATCH 20/30] output password length only in checkConfigs --- .../authentication/JdbcAuthenticationProviderImpl.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index e06bd49e293..8121b9dbd81 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -99,8 +99,8 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat debug(configLog("Driver Class", dbDriver.orNull)) debug(configLog("JDBC URL", dbUrl.orNull)) - debug(configLog("Database Username", dbUserName.orNull)) - debug(configLog("Database Password", dbPassword.orNull)) + debug(configLog("Database username", dbUserName.orNull)) + debug(configLog("Database password length", dbPassword.getOrElse("").length.toString)) debug(configLog("Query SQL", querySql.orNull)) // Check if JDBC parameters valid From 3a4d5fe9f9e6b305565bb58d68a1c1960f854a92 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 15:48:50 +0800 Subject: [PATCH 21/30] update checkConfigs() signature --- .../authentication/JdbcAuthenticationProviderImpl.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 8121b9dbd81..98591de8ef0 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -94,7 +94,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - private def checkConfigs: Unit = { + private def checkConfigs(): Unit = { def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" debug(configLog("Driver Class", dbDriver.orNull)) @@ -175,9 +175,9 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @return */ private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) From a4fe582faea9bd7a1a0b30932ebd77dc7ff25aa1 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Tue, 16 Aug 2022 16:25:11 +0800 Subject: [PATCH 22/30] refactor connection creation on using HikariDataSource in HikariCP. add HikariCP dependencies to kyuubi-common --- kyuubi-common/pom.xml | 5 ++ .../JdbcAuthenticationProviderImpl.scala | 83 ++++++++++--------- .../JdbcAuthenticationProviderImplSuite.scala | 18 ++-- 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/kyuubi-common/pom.xml b/kyuubi-common/pom.xml index 6109244da3f..73cdf3b76b7 100644 --- a/kyuubi-common/pom.xml +++ b/kyuubi-common/pom.xml @@ -114,6 +114,11 @@ jackson-databind + + com.zaxxer + HikariCP + + org.apache.hadoop hadoop-minikdc diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 98591de8ef0..9635b095a00 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -17,9 +17,11 @@ package org.apache.kyuubi.service.authentication -import java.sql.{Connection, DriverManager, PreparedStatement, Statement} +import java.sql.{Connection, PreparedStatement, Statement} +import java.util.Properties import javax.security.sasl.AuthenticationException +import com.zaxxer.hikari.{HikariConfig, HikariDataSource} import org.apache.commons.lang3.StringUtils import org.apache.kyuubi.Logging @@ -29,16 +31,20 @@ import org.apache.kyuubi.config.KyuubiConf._ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticationProvider with Logging { - private val dbDriver = conf.get(AUTHENTICATION_JDBC_DRIVER) - private val dbUrl = conf.get(AUTHENTICATION_JDBC_URL) - private val dbUserName = conf.get(AUTHENTICATION_JDBC_USERNAME) - private val dbPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD) - private val querySql = conf.get(AUTHENTICATION_JDBC_QUERY) + private val driverClass = conf.get(AUTHENTICATION_JDBC_DRIVER) + private val jdbcUrl = conf.get(AUTHENTICATION_JDBC_URL) + private val jdbcUsername = conf.get(AUTHENTICATION_JDBC_USERNAME) + private val jdbcUserPassword = conf.get(AUTHENTICATION_JDBC_PASSWORD) + private val authQuerySql = conf.get(AUTHENTICATION_JDBC_QUERY) private val SQL_PLACEHOLDER_REGEX = """\$\{.+?}""".r private val USERNAME_SQL_PLACEHOLDER = "${username}" private val PASSWORD_SQL_PLACEHOLDER = "${password}" + checkJdbcConfigs() + + private[kyuubi] val hikariDataSource = getHikariDataSource() + /** * The authenticate method is called by the Kyuubi Server authentication layer * to authenticate users for their requests. @@ -61,17 +67,13 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat s" or contains blank space") } - checkConfigs - - loadJdbcDriverClass - var connection: Connection = null var queryStatement: PreparedStatement = null try { - connection = DriverManager.getConnection(dbUrl.get, dbUserName.orNull, dbPassword.orNull) + connection = hikariDataSource.getConnection - queryStatement = getAndPrepareStatement(connection, user, password) + queryStatement = getAndPrepareQueryStatement(connection, user, password) val resultSet = queryStatement.executeQuery() @@ -94,33 +96,33 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - private def checkConfigs(): Unit = { + private def checkJdbcConfigs(): Unit = { def configLog(config: String, value: String): String = s"JDBCAuthConfig: $config = '$value'" - debug(configLog("Driver Class", dbDriver.orNull)) - debug(configLog("JDBC URL", dbUrl.orNull)) - debug(configLog("Database username", dbUserName.orNull)) - debug(configLog("Database password length", dbPassword.getOrElse("").length.toString)) - debug(configLog("Query SQL", querySql.orNull)) + debug(configLog("Driver Class", driverClass.orNull)) + debug(configLog("JDBC URL", jdbcUrl.orNull)) + debug(configLog("Database username", jdbcUsername.orNull)) + debug(configLog("Database password length", jdbcUserPassword.getOrElse("").length.toString)) + debug(configLog("Query SQL", authQuerySql.orNull)) // Check if JDBC parameters valid - if (dbDriver.isEmpty) { + if (driverClass.isEmpty) { throw new IllegalArgumentException("JDBC driver class is not configured.") } - if (dbUrl.isEmpty) { + if (jdbcUrl.isEmpty) { throw new IllegalArgumentException("JDBC url is not configured") } - if (dbUserName.isEmpty || dbPassword.isEmpty) { + if (jdbcUsername.isEmpty || jdbcUserPassword.isEmpty) { throw new IllegalArgumentException("JDBC username or password is not configured") } // Check Query SQL - if (querySql.isEmpty) { + if (authQuerySql.isEmpty) { throw new IllegalArgumentException("Query SQL is not configured") } - val querySqlInLowerCase = querySql.get.trim.toLowerCase + val querySqlInLowerCase = authQuerySql.get.trim.toLowerCase if (!querySqlInLowerCase.startsWith("select")) { // allow select query sql only throw new IllegalArgumentException("Query SQL must start with \"SELECT\""); } @@ -132,17 +134,6 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - private def loadJdbcDriverClass: Unit = { - // Load Driver Class - try { - Class.forName(dbDriver.get) - } catch { - case e: ClassNotFoundException => - error(s"JDBC Driver class not found: $dbDriver") - throw e; - } - } - /** * Extract all placeholders from query and put them into a list. * @@ -174,15 +165,15 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat * @param password * @return */ - private def getAndPrepareStatement( - connection: Connection, - user: String, - password: String): PreparedStatement = { + private def getAndPrepareQueryStatement( + connection: Connection, + user: String, + password: String): PreparedStatement = { // Replace placeholders by "?" and prepare the statement - val stmt = connection.prepareStatement(getPreparedSql(querySql.get)) + val stmt = connection.prepareStatement(getPreparedSql(authQuerySql.get)) // Extract placeholder list and use its order to pass parameters - val placeholderList: List[String] = getPlaceholderList(querySql.get) + val placeholderList: List[String] = getPlaceholderList(authQuerySql.get) for (i <- placeholderList.indices) { val param = placeholderList(i) match { case USERNAME_SQL_PLACEHOLDER => user @@ -229,4 +220,16 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } } + + private def getHikariDataSource(): HikariDataSource = { + val datasourceProperties = new Properties() + val hikariConfig = new HikariConfig(datasourceProperties) + hikariConfig.setDriverClassName(driverClass.orNull) + hikariConfig.setJdbcUrl(jdbcUrl.orNull) + hikariConfig.setUsername(jdbcUsername.orNull) + hikariConfig.setPassword(jdbcUserPassword.orNull) + hikariConfig.setPoolName("jdbc-auth-pool") + + new HikariDataSource(hikariConfig) + } } diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index afae8443586..df6421a94a7 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -98,39 +98,33 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_URL) - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e5 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e5 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e5.getMessage.contains("JDBC url is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_USERNAME) - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e6 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e6 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e6.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_PASSWORD) - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e7 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e7 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e7.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_QUERY) - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e8 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e8 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e8.getMessage.contains("Query SQL is not configured")) conf.set( AUTHENTICATION_JDBC_QUERY, "INSERT INTO user_auth (username, password) " + " VALUES ('demouser','demopassword'); ") - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e9 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e9 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e9.getMessage.contains("Query SQL must start with \"SELECT\"")) conf.unset(AUTHENTICATION_JDBC_URL) - providerImpl = new JdbcAuthenticationProviderImpl(conf) - val e10 = intercept[IllegalArgumentException](providerImpl.authenticate(authUser, authPasswd)) + val e10 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) assert(e10.getMessage.contains("JDBC url is not configured")) } From 543c66cb61b7432848c7e81eb1ddf83af69283be Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 01:10:36 +0800 Subject: [PATCH 23/30] prefer scala style string usage --- .../JdbcAuthenticationProviderImplSuite.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index df6421a94a7..ad3fce9d888 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -50,16 +50,16 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { jdbcUrl = s"jdbc:derby:;databaseName=$authDb;create=true" conn = DriverManager.getConnection( - jdbcUrl - + ";user=" + dbUser - + ";password=" + dbPasswd, + s"$jdbcUrl;user=$dbUser;password=$dbPasswd", datasourceProperties) - conn.prepareStatement("CREATE SCHEMA " + dbUser).execute(); + conn.prepareStatement(s"CREATE SCHEMA $dbUser").execute - conn.prepareStatement("CREATE TABLE user_auth (" + - "username VARCHAR(64) NOT NULL PRIMARY KEY, " + - "passwd VARCHAR(64))").execute(); + conn.prepareStatement( + """CREATE TABLE user_auth ( + |username VARCHAR(64) NOT NULL PRIMARY KEY, + |passwd VARCHAR(64))""".stripMargin + ).execute(); val insertStmt = conn.prepareStatement("INSERT INTO user_auth " + "(username, passwd) VALUES (?,?)") From 6765affb1c5303c43a4e31e75209be03fbad4af8 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 01:17:42 +0800 Subject: [PATCH 24/30] changed to use in-memory derby db for test --- .../JdbcAuthenticationProviderImplSuite.scala | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index ad3fce9d888..0dc06e28e71 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -17,12 +17,11 @@ package org.apache.kyuubi.service.authentication -import java.nio.file.Path import java.sql.{Connection, DriverManager} import java.util.Properties import javax.security.sasl.AuthenticationException -import org.apache.kyuubi.{KyuubiFunSuite, Utils} +import org.apache.kyuubi.KyuubiFunSuite import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ @@ -36,19 +35,15 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { protected var conf = new KyuubiConf() var conn: Connection = _ - var authDb: Path = _ + var authDbName: String = "auth_db" override def beforeAll(): Unit = { - super.beforeAll() - + // init db val datasourceProperties = new Properties() datasourceProperties.put("user", dbUser) datasourceProperties.put("password", dbPasswd) - authDb = Utils.createTempDir(namePrefix = getClass.getSimpleName) - authDb.toFile.delete() - - jdbcUrl = s"jdbc:derby:;databaseName=$authDb;create=true" + jdbcUrl = s"jdbc:derby:memory:$authDbName;create=true" conn = DriverManager.getConnection( s"$jdbcUrl;user=$dbUser;password=$dbPasswd", datasourceProperties) @@ -58,8 +53,7 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { conn.prepareStatement( """CREATE TABLE user_auth ( |username VARCHAR(64) NOT NULL PRIMARY KEY, - |passwd VARCHAR(64))""".stripMargin - ).execute(); + |passwd VARCHAR(64))""".stripMargin).execute(); val insertStmt = conn.prepareStatement("INSERT INTO user_auth " + "(username, passwd) VALUES (?,?)") @@ -68,14 +62,16 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { insertStmt.execute(); conf = genJdbcAuthConfigs + + super.beforeAll() } override def afterAll(): Unit = { super.afterAll() - // shutdown derby database + // cleanup db try { - DriverManager.getConnection(s"jdbc:derby:;databaseName=$authDb;shutdown=true") + DriverManager.getConnection(s"jdbc:derby:memory:$authDbName;shutdown=true") } catch { case e: Throwable => } From 77f5f86b425d77aec11369ee2e1b2b9684e5eb1b Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 01:30:18 +0800 Subject: [PATCH 25/30] remove unuseful comment --- .../authentication/JdbcAuthenticationProviderImpl.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index 9635b095a00..d24e94deee6 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -193,12 +193,6 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat stmt } - /** - * Gracefully close DB connection - * - * @param connection - * @param statement - */ private def closeDbConnection(connection: Connection, statement: Statement): Unit = { // Close statement if (statement != null && !statement.isClosed) { From a9404fa30e9b6a23a2117599188448573b3f9a91 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 01:34:04 +0800 Subject: [PATCH 26/30] use {} for intercept --- .../JdbcAuthenticationProviderImplSuite.scala | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index 0dc06e28e71..480543c0edf 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -82,45 +82,50 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { providerImpl.authenticate(authUser, authPasswd) - val e1 = intercept[AuthenticationException](providerImpl.authenticate("", "")) + val e1 = intercept[AuthenticationException]{ + providerImpl.authenticate("", "") + } assert(e1.getMessage.contains("user is null")) - val e2 = intercept[AuthenticationException](providerImpl.authenticate("kyuubi", "")) + val e2 = intercept[AuthenticationException]{ + providerImpl.authenticate("kyuubi", "") + } assert(e2.getMessage.contains("password is null")) - val e4 = intercept[AuthenticationException]( - providerImpl.authenticate(authPasswd, "pass")) + val e4 = intercept[AuthenticationException]{ + providerImpl.authenticate(authPasswd, "pass") + } assert(e4.isInstanceOf[AuthenticationException]) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_URL) - val e5 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e5 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e5.getMessage.contains("JDBC url is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_USERNAME) - val e6 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e6 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e6.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_PASSWORD) - val e7 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e7 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e7.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_QUERY) - val e8 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e8 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e8.getMessage.contains("Query SQL is not configured")) conf.set( AUTHENTICATION_JDBC_QUERY, "INSERT INTO user_auth (username, password) " + " VALUES ('demouser','demopassword'); ") - val e9 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e9 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e9.getMessage.contains("Query SQL must start with \"SELECT\"")) conf.unset(AUTHENTICATION_JDBC_URL) - val e10 = intercept[IllegalArgumentException](new JdbcAuthenticationProviderImpl(conf)) + val e10 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} assert(e10.getMessage.contains("JDBC url is not configured")) } From 6fc42bf3c8459ab79b5b597bd6b6bf7783ea6e91 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 01:36:03 +0800 Subject: [PATCH 27/30] code styling --- .../JdbcAuthenticationProviderImplSuite.scala | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index 480543c0edf..8a02cc50447 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -82,50 +82,50 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { providerImpl.authenticate(authUser, authPasswd) - val e1 = intercept[AuthenticationException]{ + val e1 = intercept[AuthenticationException] { providerImpl.authenticate("", "") } assert(e1.getMessage.contains("user is null")) - val e2 = intercept[AuthenticationException]{ + val e2 = intercept[AuthenticationException] { providerImpl.authenticate("kyuubi", "") } assert(e2.getMessage.contains("password is null")) - val e4 = intercept[AuthenticationException]{ + val e4 = intercept[AuthenticationException] { providerImpl.authenticate(authPasswd, "pass") } assert(e4.isInstanceOf[AuthenticationException]) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_URL) - val e5 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e5 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e5.getMessage.contains("JDBC url is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_USERNAME) - val e6 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e6 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e6.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_PASSWORD) - val e7 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e7 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e7.getMessage.contains("JDBC username or password is not configured")) conf = genJdbcAuthConfigs conf.unset(AUTHENTICATION_JDBC_QUERY) - val e8 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e8 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e8.getMessage.contains("Query SQL is not configured")) conf.set( AUTHENTICATION_JDBC_QUERY, "INSERT INTO user_auth (username, password) " + " VALUES ('demouser','demopassword'); ") - val e9 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e9 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e9.getMessage.contains("Query SQL must start with \"SELECT\"")) conf.unset(AUTHENTICATION_JDBC_URL) - val e10 = intercept[IllegalArgumentException]{new JdbcAuthenticationProviderImpl(conf)} + val e10 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e10.getMessage.contains("JDBC url is not configured")) } From e9af0966987de884d6b02354fb89628fc244e08c Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 02:02:10 +0800 Subject: [PATCH 28/30] use clone instead of repeatly generating configs --- .../JdbcAuthenticationProviderImplSuite.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index 8a02cc50447..b5690f1db58 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -78,8 +78,9 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { } test("authenticate tests") { - var providerImpl = new JdbcAuthenticationProviderImpl(conf) + val unchangedConf = genJdbcAuthConfigs + val providerImpl = new JdbcAuthenticationProviderImpl(conf) providerImpl.authenticate(authUser, authPasswd) val e1 = intercept[AuthenticationException] { @@ -97,22 +98,22 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { } assert(e4.isInstanceOf[AuthenticationException]) - conf = genJdbcAuthConfigs + conf = unchangedConf.clone() conf.unset(AUTHENTICATION_JDBC_URL) val e5 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e5.getMessage.contains("JDBC url is not configured")) - conf = genJdbcAuthConfigs + conf = unchangedConf.clone() conf.unset(AUTHENTICATION_JDBC_USERNAME) val e6 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e6.getMessage.contains("JDBC username or password is not configured")) - conf = genJdbcAuthConfigs + conf = unchangedConf.clone() conf.unset(AUTHENTICATION_JDBC_PASSWORD) val e7 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e7.getMessage.contains("JDBC username or password is not configured")) - conf = genJdbcAuthConfigs + conf = unchangedConf.clone() conf.unset(AUTHENTICATION_JDBC_QUERY) val e8 = intercept[IllegalArgumentException] { new JdbcAuthenticationProviderImpl(conf) } assert(e8.getMessage.contains("Query SQL is not configured")) From d5f43e0e6d5be884a21bfd0bdfc013b831428e86 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 02:03:33 +0800 Subject: [PATCH 29/30] remove unuseful logs for unrecognized placeholder error --- .../authentication/JdbcAuthenticationProviderImpl.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index d24e94deee6..d0df23bd943 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -179,8 +179,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat case USERNAME_SQL_PLACEHOLDER => user case PASSWORD_SQL_PLACEHOLDER => password case otherPlaceholder => - error(s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") - throw new IllegalStateException( + throw new IllegalArgumentException( s"Unrecognized Placeholder In Query SQL: $otherPlaceholder") } From 17403b3329439aca068dfaee6cf91d9027471b69 Mon Sep 17 00:00:00 2001 From: liangbowen Date: Wed, 17 Aug 2022 09:27:19 +0800 Subject: [PATCH 30/30] cleanup docs --- .../JdbcAuthenticationProviderImpl.scala | 50 +++++-------------- .../JdbcAuthenticationProviderImplSuite.scala | 8 +-- 2 files changed, 17 insertions(+), 41 deletions(-) diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala index d0df23bd943..c4b0abdc7ab 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImpl.scala @@ -43,7 +43,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat checkJdbcConfigs() - private[kyuubi] val hikariDataSource = getHikariDataSource() + private[kyuubi] val hikariDataSource = getHikariDataSource /** * The authenticate method is called by the Kyuubi Server authentication layer @@ -78,12 +78,12 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat val resultSet = queryStatement.executeQuery() if (resultSet == null || !resultSet.next()) { - // Auth failed + // auth failed throw new AuthenticationException(s"Password does not match or no such user. user:" + s" $user , password length: ${password.length}") } - // Auth passed + // auth passed } catch { case e: AuthenticationException => @@ -134,45 +134,26 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - /** - * Extract all placeholders from query and put them into a list. - * - * @param sql - * @return - */ private def getPlaceholderList(sql: String): List[String] = { SQL_PLACEHOLDER_REGEX.findAllMatchIn(sql) .map(m => m.matched) .toList } - /** - * Replace all placeholders as "?" - * - * @param sql - * @return - */ - private def getPreparedSql(sql: String): String = { - SQL_PLACEHOLDER_REGEX.replaceAllIn(sql, "?") - } - - /** - * prepare the final query statement - * by replacing placeholder in query sql with user and password - * - * @param connection - * @param user - * @param password - * @return - */ private def getAndPrepareQueryStatement( connection: Connection, user: String, password: String): PreparedStatement = { - // Replace placeholders by "?" and prepare the statement - val stmt = connection.prepareStatement(getPreparedSql(authQuerySql.get)) - // Extract placeholder list and use its order to pass parameters + val preparedSql: String = { + SQL_PLACEHOLDER_REGEX.replaceAllIn(authQuerySql.get, "?") + } + debug(s"prepared auth query sql: $preparedSql") + + val stmt = connection.prepareStatement(preparedSql) + stmt.setMaxRows(1) // minimum result size required for authentication + + // Extract placeholder list and fill parameters to placeholders val placeholderList: List[String] = getPlaceholderList(authQuerySql.get) for (i <- placeholderList.indices) { val param = placeholderList(i) match { @@ -186,14 +167,10 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat stmt.setString(i + 1, param) } - // Client side limit 1 - stmt.setMaxRows(1) - stmt } private def closeDbConnection(connection: Connection, statement: Statement): Unit = { - // Close statement if (statement != null && !statement.isClosed) { try { statement.close() @@ -203,7 +180,6 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - // Close connection if (connection != null && !connection.isClosed) { try { connection.close() @@ -214,7 +190,7 @@ class JdbcAuthenticationProviderImpl(conf: KyuubiConf) extends PasswdAuthenticat } } - private def getHikariDataSource(): HikariDataSource = { + private def getHikariDataSource: HikariDataSource = { val datasourceProperties = new Properties() val hikariConfig = new HikariConfig(datasourceProperties) hikariConfig.setDriverClassName(driverClass.orNull) diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala index b5690f1db58..617a64fbae9 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/JdbcAuthenticationProviderImplSuite.scala @@ -26,8 +26,8 @@ import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { - protected val dbUser: String = "bowenliang123" - protected val dbPasswd: String = "bowenliang123" + protected val dbUser: String = "liangbowen" + protected val dbPasswd: String = "liangbowen" protected var jdbcUrl: String = _ protected val authUser: String = "liangtiancheng" @@ -89,12 +89,12 @@ class JdbcAuthenticationProviderImplSuite extends KyuubiFunSuite { assert(e1.getMessage.contains("user is null")) val e2 = intercept[AuthenticationException] { - providerImpl.authenticate("kyuubi", "") + providerImpl.authenticate(authUser, "") } assert(e2.getMessage.contains("password is null")) val e4 = intercept[AuthenticationException] { - providerImpl.authenticate(authPasswd, "pass") + providerImpl.authenticate(authUser, "wrong_password") } assert(e4.isInstanceOf[AuthenticationException])