diff --git a/docs/deployment/settings.md b/docs/deployment/settings.md index 1d68c86ccd3..c3e35ce5675 100644 --- a/docs/deployment/settings.md +++ b/docs/deployment/settings.md @@ -220,13 +220,6 @@ Key | Default | Meaning | Type | Since kyuubi.engine.pool.name|
engine-pool
|
The name of engine pool.
|
string
|
1.5.0
kyuubi.engine.pool.size|
-1
|
The size of engine pool. Note that, if the size is less than 1, the engine pool will not be enabled; otherwise, the size of the engine pool will be min(this, kyuubi.engine.pool.size.threshold).
|
int
|
1.4.0
kyuubi.engine.pool.size.threshold|
9
|
This parameter is introduced as a server-side parameter, and controls the upper limit of the engine pool.
|
int
|
1.4.0
-kyuubi.engine.security.crypto.cipher|
AES/CBC/PKCS5PADDING
|
The cipher transformation to use for encrypting engine access token.
|
string
|
1.5.0
-kyuubi.engine.security.crypto.ivLength|
16
|
Initial vector length, in bytes.
|
int
|
1.5.0
-kyuubi.engine.security.crypto.keyAlgorithm|
AES
|
The algorithm for generated secret keys.
|
string
|
1.5.0
-kyuubi.engine.security.crypto.keyLength|
128
|
The length in bits of the encryption key to generate. Valid values are 128, 192 and 256
|
int
|
1.5.0
-kyuubi.engine.security.enabled|
false
|
Whether to enable the internal secure access between Kyuubi server and engine.
|
boolean
|
1.5.0
-kyuubi.engine.security.secret.provider|
org.apache.kyuubi.service.authentication.ZooKeeperEngineSecuritySecretProviderImpl
|
The class used to manage the engine security secret. This class must be a subclass of EngineSecuritySecretProvider.
|
string
|
1.5.0
-kyuubi.engine.security.token.max.lifetime|
PT10M
|
The max lifetime of the token used for secure access between Kyuubi server and engine.
|
duration
|
1.5.0
kyuubi.engine.session.initialize.sql|
|
SemiColon-separated list of SQL statements to be initialized in the newly created engine session before queries. This configuration can not be used in JDBC url due to the limitation of Beeline/JDBC driver.
|
seq
|
1.3.0
kyuubi.engine.share.level|
USER
|
Engines will be shared in different levels, available configs are:
|
string
|
1.2.0
kyuubi.engine.share.level.sub.domain|
<undefined>
|
(deprecated) - Using kyuubi.engine.share.level.subdomain instead
|
string
|
1.2.0
@@ -292,7 +285,6 @@ Key | Default | Meaning | Type | Since kyuubi.ha.zookeeper.connection.retry.policy|
EXPONENTIAL_BACKOFF
|
The retry policy for connecting to the zookeeper ensemble, all candidates are:
|
string
|
1.0.0
kyuubi.ha.zookeeper.connection.timeout|
15000
|
The timeout(ms) of creating the connection to the zookeeper ensemble
|
int
|
1.0.0
kyuubi.ha.zookeeper.engine.auth.type|
NONE
|
The type of zookeeper authentication for engine, all candidates are
|
string
|
1.3.2
-kyuubi.ha.zookeeper.engine.secure.secret.node|
<undefined>
|
The zk node contains the secret that used for internal secure between Kyuubi server and Kyuubi engine, please make sure that it is only visible for Kyuubi.
|
string
|
1.5.0
kyuubi.ha.zookeeper.namespace|
kyuubi
|
The root directory for the service to deploy its instance uri
|
string
|
1.0.0
kyuubi.ha.zookeeper.node.creation.timeout|
PT2M
|
Timeout for creating zookeeper node
|
duration
|
1.2.0
kyuubi.ha.zookeeper.publish.configs|
false
|
When set to true, publish Kerberos configs to Zookeeper.Note that the Hive driver needs to be greater than 1.3 or 2.0 or apply HIVE-11581 patch.
|
boolean
|
1.4.0
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 b058f04789f..f9ef694e002 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 @@ -1250,21 +1250,26 @@ object KyuubiConf { val ENGINE_SECURITY_ENABLED: ConfigEntry[Boolean] = buildConf("kyuubi.engine.security.enabled") - .doc("Whether to enable the internal secure access between Kyuubi server and engine.") + .internal + .doc("Whether to enable the internal secure access. Before 1.6.0, it is used for the secure" + + " access between kyuubi server and kyuubi engine. Since 1.6.0, kyuubi supports internal" + + " secure across kyuubi server instances.") .version("1.5.0") .booleanConf .createWithDefault(false) val ENGINE_SECURITY_TOKEN_MAX_LIFETIME: ConfigEntry[Long] = buildConf("kyuubi.engine.security.token.max.lifetime") - .doc("The max lifetime of the token used for secure access between Kyuubi server and engine.") + .internal + .doc("The max lifetime of the token used for internal secure access.") .version("1.5.0") .timeConf .createWithDefault(Duration.ofMinutes(10).toMillis) val ENGINE_SECURITY_SECRET_PROVIDER: ConfigEntry[String] = buildConf("kyuubi.engine.security.secret.provider") - .doc("The class used to manage the engine security secret. This class must be a " + + .internal + .doc("The class used to manage the internal security secret. This class must be a " + "subclass of EngineSecuritySecretProvider.") .version("1.5.0") .stringConf @@ -1273,6 +1278,7 @@ object KyuubiConf { val ENGINE_SECURITY_CRYPTO_KEY_LENGTH: ConfigEntry[Int] = buildConf("kyuubi.engine.security.crypto.keyLength") + .internal .doc("The length in bits of the encryption key to generate. " + "Valid values are 128, 192 and 256") .version("1.5.0") @@ -1282,6 +1288,7 @@ object KyuubiConf { val ENGINE_SECURITY_CRYPTO_IV_LENGTH: ConfigEntry[Int] = buildConf("kyuubi.engine.security.crypto.ivLength") + .internal .doc("Initial vector length, in bytes.") .version("1.5.0") .intConf @@ -1289,6 +1296,7 @@ object KyuubiConf { val ENGINE_SECURITY_CRYPTO_KEY_ALGORITHM: ConfigEntry[String] = buildConf("kyuubi.engine.security.crypto.keyAlgorithm") + .internal .doc("The algorithm for generated secret keys.") .version("1.5.0") .stringConf @@ -1296,7 +1304,8 @@ object KyuubiConf { val ENGINE_SECURITY_CRYPTO_CIPHER_TRANSFORMATION: ConfigEntry[String] = buildConf("kyuubi.engine.security.crypto.cipher") - .doc("The cipher transformation to use for encrypting engine access token.") + .internal + .doc("The cipher transformation to use for encrypting internal access token.") .version("1.5.0") .stringConf .createWithDefault("AES/CBC/PKCS5PADDING") diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecureAuthenticationProviderImpl.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecureAuthenticationProviderImpl.scala index 709081dd9f8..64e1b982f4e 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecureAuthenticationProviderImpl.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecureAuthenticationProviderImpl.scala @@ -19,6 +19,6 @@ package org.apache.kyuubi.service.authentication class EngineSecureAuthenticationProviderImpl extends PasswdAuthenticationProvider { override def authenticate(user: String, password: String): Unit = { - EngineSecurityAccessor.get().authToken(password) + InternalSecurityAccessor.get().authToken(password) } } diff --git a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessor.scala b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessor.scala similarity index 92% rename from kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessor.scala rename to kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessor.scala index 98cf6a82155..3c77634ef8e 100644 --- a/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessor.scala +++ b/kyuubi-common/src/main/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessor.scala @@ -24,7 +24,7 @@ import org.apache.kyuubi.{KyuubiSQLException, Logging} import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf._ -class EngineSecurityAccessor(conf: KyuubiConf, val isServer: Boolean) { +class InternalSecurityAccessor(conf: KyuubiConf, val isServer: Boolean) { val cryptoKeyLengthBytes = conf.get(ENGINE_SECURITY_CRYPTO_KEY_LENGTH) / java.lang.Byte.SIZE val cryptoIvLength = conf.get(ENGINE_SECURITY_CRYPTO_IV_LENGTH) val cryptoKeyAlgorithm = conf.get(ENGINE_SECURITY_CRYPTO_KEY_ALGORITHM) @@ -109,16 +109,16 @@ class EngineSecurityAccessor(conf: KyuubiConf, val isServer: Boolean) { } } -object EngineSecurityAccessor extends Logging { - @volatile private var _engineSecurityAccessor: EngineSecurityAccessor = _ +object InternalSecurityAccessor extends Logging { + @volatile private var _engineSecurityAccessor: InternalSecurityAccessor = _ def initialize(conf: KyuubiConf, isServer: Boolean): Unit = { if (_engineSecurityAccessor == null) { - _engineSecurityAccessor = new EngineSecurityAccessor(conf, isServer) + _engineSecurityAccessor = new InternalSecurityAccessor(conf, isServer) } } - def get(): EngineSecurityAccessor = { + def get(): InternalSecurityAccessor = { _engineSecurityAccessor } } 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 b532825172e..f943def8616 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 @@ -57,7 +57,7 @@ class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer: Boolean = true) ex } if (conf.get(ENGINE_SECURITY_ENABLED)) { - EngineSecurityAccessor.initialize(conf, isServer) + InternalSecurityAccessor.initialize(conf, isServer) } private def getSaslProperties: java.util.Map[String, String] = { diff --git a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessorSuite.scala b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessorSuite.scala similarity index 89% rename from kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessorSuite.scala rename to kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessorSuite.scala index 61bdef8fd6f..e6c4c850690 100644 --- a/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/EngineSecurityAccessorSuite.scala +++ b/kyuubi-common/src/test/scala/org/apache/kyuubi/service/authentication/InternalSecurityAccessorSuite.scala @@ -20,7 +20,7 @@ package org.apache.kyuubi.service.authentication import org.apache.kyuubi.{KyuubiFunSuite, KyuubiSQLException} import org.apache.kyuubi.config.KyuubiConf -class EngineSecurityAccessorSuite extends KyuubiFunSuite { +class InternalSecurityAccessorSuite extends KyuubiFunSuite { private val conf = KyuubiConf() conf.set( KyuubiConf.ENGINE_SECURITY_SECRET_PROVIDER, @@ -31,7 +31,7 @@ class EngineSecurityAccessorSuite extends KyuubiFunSuite { val newConf = conf.clone newConf.set(KyuubiConf.ENGINE_SECURITY_CRYPTO_CIPHER_TRANSFORMATION, cipher) - val secureAccessor = new EngineSecurityAccessor(newConf, true) + val secureAccessor = new InternalSecurityAccessor(newConf, true) val value = "tokenToEncrypt" val encryptedValue = secureAccessor.encrypt(value) assert(secureAccessor.decrypt(encryptedValue) === value) @@ -40,7 +40,7 @@ class EngineSecurityAccessorSuite extends KyuubiFunSuite { secureAccessor.authToken(token) intercept[KyuubiSQLException](secureAccessor.authToken("invalidToken")) - val engineSecureAccessor = new EngineSecurityAccessor(newConf, false) + val engineSecureAccessor = new InternalSecurityAccessor(newConf, false) engineSecureAccessor.authToken(token) } } diff --git a/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala index 151f9a823b1..317cadaf585 100644 --- a/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala +++ b/kyuubi-ha/src/main/scala/org/apache/kyuubi/ha/HighAvailabilityConf.scala @@ -153,8 +153,9 @@ object HighAvailabilityConf { val HA_ZK_ENGINE_SECURE_SECRET_NODE: OptionalConfigEntry[String] = buildConf("kyuubi.ha.zookeeper.engine.secure.secret.node") - .doc("The zk node contains the secret that used for internal secure between Kyuubi server " + - "and Kyuubi engine, please make sure that it is only visible for Kyuubi.") + .internal + .doc("The zk node contains the secret that used for internal secure, please make sure " + + "that it is only visible for Kyuubi.") .version("1.5.0") .stringConf .createOptional diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthSchemes.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthSchemes.scala index c7924ab718c..9f73b3af859 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthSchemes.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthSchemes.scala @@ -20,5 +20,5 @@ package org.apache.kyuubi.server.http.authentication object AuthSchemes extends Enumeration { type AuthScheme = Value - val BASIC, NEGOTIATE = Value + val BASIC, NEGOTIATE, KYUUBI_INTERNAL = Value } diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala index 360d8c3369d..81f347f34f8 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationFilter.scala @@ -28,7 +28,7 @@ import org.apache.hadoop.security.authentication.client.AuthenticationException import org.apache.kyuubi.Logging import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf.AUTHENTICATION_METHOD -import org.apache.kyuubi.service.authentication.AuthTypes +import org.apache.kyuubi.service.authentication.{AuthTypes, InternalSecurityAccessor} import org.apache.kyuubi.service.authentication.AuthTypes.{KERBEROS, NOSASL} class AuthenticationFilter(conf: KyuubiConf) extends Filter with Logging { @@ -72,6 +72,10 @@ class AuthenticationFilter(conf: KyuubiConf) extends Filter with Logging { val basicHandler = new BasicAuthenticationHandler(basicAuthType) addAuthHandler(basicHandler) } + if (InternalSecurityAccessor.get() != null) { + val internalHandler = new KyuubiInternalAuthenticationHandler + addAuthHandler(internalHandler) + } super.init(filterConfig) } diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala index 83023ddc587..16dc607b884 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiHttpAuthenticationFactory.scala @@ -26,7 +26,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.config.KyuubiConf.{AUTHENTICATION_METHOD, ENGINE_SECURITY_ENABLED} -import org.apache.kyuubi.service.authentication.{AuthTypes, EngineSecurityAccessor} +import org.apache.kyuubi.service.authentication.{AuthTypes, InternalSecurityAccessor} import org.apache.kyuubi.service.authentication.AuthTypes.KERBEROS class KyuubiHttpAuthenticationFactory(conf: KyuubiConf) { @@ -35,7 +35,7 @@ class KyuubiHttpAuthenticationFactory(conf: KyuubiConf) { private val ugi = UserGroupInformation.getCurrentUser if (conf.get(ENGINE_SECURITY_ENABLED)) { - EngineSecurityAccessor.initialize(conf, true) + InternalSecurityAccessor.initialize(conf, true) } private[kyuubi] val httpHandlerWrapperFactory = diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiInternalAuthenticationHandler.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiInternalAuthenticationHandler.scala new file mode 100644 index 00000000000..7af6389ccee --- /dev/null +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KyuubiInternalAuthenticationHandler.scala @@ -0,0 +1,66 @@ +/* + * 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.server.http.authentication + +import java.nio.charset.Charset +import java.util.Base64 +import javax.servlet.http.{HttpServletRequest, HttpServletResponse} + +import org.apache.kyuubi.Logging +import org.apache.kyuubi.config.KyuubiConf +import org.apache.kyuubi.server.http.authentication.AuthSchemes.AuthScheme +import org.apache.kyuubi.service.authentication.InternalSecurityAccessor + +class KyuubiInternalAuthenticationHandler extends AuthenticationHandler with Logging { + import AuthenticationHandler._ + + private var conf: KyuubiConf = _ + override val authScheme: AuthScheme = AuthSchemes.KYUUBI_INTERNAL + private val internalSecurityAccessor = InternalSecurityAccessor.get() + + override def init(conf: KyuubiConf): Unit = { + this.conf = conf + } + + override def authenticationSupported: Boolean = { + internalSecurityAccessor != null + } + + override def authenticate( + request: HttpServletRequest, + response: HttpServletResponse): String = { + var authUser: String = null + val authorization = getAuthorization(request) + val inputToken = Option(authorization).map(a => Base64.getDecoder.decode(a.getBytes())) + .getOrElse(Array.empty[Byte]) + val creds = new String(inputToken, Charset.forName("UTF-8")).split(":") + + if (creds.size < 2 || creds(0).trim.isEmpty || creds(1).trim.isEmpty) { + response.setHeader(WWW_AUTHENTICATE, authScheme.toString) + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED) + } else { + val Seq(user, password) = creds.toSeq.take(2) + internalSecurityAccessor.authToken(password) + response.setStatus(HttpServletResponse.SC_OK) + authUser = user + } + authUser + } + + override def destroy(): Unit = {} +} diff --git a/kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionImpl.scala b/kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionImpl.scala index c0c5b2705bb..b70a3f1bc16 100644 --- a/kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionImpl.scala +++ b/kyuubi-server/src/main/scala/org/apache/kyuubi/session/KyuubiSessionImpl.scala @@ -33,7 +33,7 @@ import org.apache.kyuubi.metrics.MetricsConstants._ import org.apache.kyuubi.metrics.MetricsSystem import org.apache.kyuubi.operation.{Operation, OperationHandle} import org.apache.kyuubi.operation.log.OperationLog -import org.apache.kyuubi.service.authentication.EngineSecurityAccessor +import org.apache.kyuubi.service.authentication.InternalSecurityAccessor class KyuubiSessionImpl( protocol: TProtocolVersion, @@ -98,7 +98,7 @@ class KyuubiSessionImpl( val (host, port) = engine.getOrCreate(discoveryClient, extraEngineLog) val passwd = if (sessionManager.getConf.get(ENGINE_SECURITY_ENABLED)) { - EngineSecurityAccessor.get().issueToken() + InternalSecurityAccessor.get().issueToken() } else { Option(password).filter(_.nonEmpty).getOrElse("anonymous") } diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiOperationWithEngineSecurity.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiOperationWithEngineSecurity.scala index 5face684f63..63369f4b21a 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiOperationWithEngineSecurity.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiOperationWithEngineSecurity.scala @@ -21,7 +21,7 @@ import org.apache.kyuubi.WithKyuubiServer import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.ha.HighAvailabilityConf import org.apache.kyuubi.ha.client.DiscoveryClientProvider -import org.apache.kyuubi.service.authentication.{EngineSecurityAccessor, ZooKeeperEngineSecuritySecretProviderImpl} +import org.apache.kyuubi.service.authentication.{InternalSecurityAccessor, ZooKeeperEngineSecuritySecretProviderImpl} class KyuubiOperationWithEngineSecurity extends WithKyuubiServer with HiveJDBCTestHelper { import DiscoveryClientProvider._ @@ -47,7 +47,7 @@ class KyuubiOperationWithEngineSecurity extends WithKyuubiServer with HiveJDBCTe } conf.set(KyuubiConf.ENGINE_SECURITY_ENABLED, true) - EngineSecurityAccessor.initialize(conf, true) + InternalSecurityAccessor.initialize(conf, true) } test("engine security") { diff --git a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala index 8bc5b076f30..20f646af52f 100644 --- a/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala +++ b/kyuubi-server/src/test/scala/org/apache/kyuubi/operation/KyuubiRestAuthenticationSuite.scala @@ -29,10 +29,11 @@ import org.apache.hadoop.security.UserGroupInformation import org.apache.hive.service.rpc.thrift.TProtocolVersion import org.apache.kyuubi.{KerberizedTestHelper, RestFrontendTestHelper} -import org.apache.kyuubi.client.api.v1.dto.{SessionOpenCount, SessionOpenRequest} +import org.apache.kyuubi.client.api.v1.dto.{SessionHandle, SessionOpenCount, SessionOpenRequest} import org.apache.kyuubi.config.KyuubiConf import org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER -import org.apache.kyuubi.service.authentication.{UserDefineAuthenticationProviderImpl, WithLdapServer} +import org.apache.kyuubi.server.http.authentication.AuthSchemes +import org.apache.kyuubi.service.authentication._ class KyuubiRestAuthenticationSuite extends RestFrontendTestHelper with KerberizedTestHelper with WithLdapServer { @@ -71,6 +72,10 @@ class KyuubiRestAuthenticationSuite extends RestFrontendTestHelper with Kerberiz .set( KyuubiConf.AUTHENTICATION_CUSTOM_CLASS, classOf[UserDefineAuthenticationProviderImpl].getCanonicalName) + .set(KyuubiConf.ENGINE_SECURITY_ENABLED, true) + .set( + KyuubiConf.ENGINE_SECURITY_SECRET_PROVIDER, + classOf[UserDefinedEngineSecuritySecretProvider].getCanonicalName) } test("test with LDAP authorization") { @@ -156,19 +161,57 @@ class KyuubiRestAuthenticationSuite extends RestFrontendTestHelper with Kerberiz test("test with ugi wrapped open session") { UserGroupInformation.loginUserFromKeytab(testPrincipal, testKeytab) - val token = generateToken(hostName) + var token = generateToken(hostName) val sessionOpenRequest = new SessionOpenRequest( TProtocolVersion.HIVE_CLI_SERVICE_PROTOCOL_V11.getValue, "kyuubi", "pass", "localhost", - Map.empty[String, String].asJava) + Map(KyuubiConf.ENGINE_SHARE_LEVEL.key -> "CONNECTION").asJava) - val response = webTarget.path("api/v1/sessions") + var response = webTarget.path("api/v1/sessions") .request() .header(AUTHORIZATION_HEADER, s"NEGOTIATE $token") .post(Entity.entity(sessionOpenRequest, MediaType.APPLICATION_JSON_TYPE)) assert(HttpServletResponse.SC_OK == response.getStatus) + val sessionHandle = response.readEntity(classOf[SessionHandle]) + + token = generateToken(hostName) + val serializedSessionHandle = s"${sessionHandle.getPublicId}|" + + s"${sessionHandle.getSecretId}|${sessionHandle.getProtocolVersion}" + + response = webTarget.path(s"api/v1/sessions/$serializedSessionHandle") + .request() + .header(AUTHORIZATION_HEADER, s"NEGOTIATE $token") + .delete() + assert(HttpServletResponse.SC_OK == response.getStatus) + } + + test("test with internal authorization") { + val internalSecurityAccessor = InternalSecurityAccessor.get() + var encodeAuthorization = new String( + Base64.getEncoder.encode( + s"$ldapUser:${internalSecurityAccessor.issueToken()}".getBytes()), + "UTF-8") + var response = webTarget.path("api/v1/sessions/count") + .request() + .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL.toString} $encodeAuthorization") + .get() + + assert(HttpServletResponse.SC_OK == response.getStatus) + val openedSessionCount = response.readEntity(classOf[SessionOpenCount]) + assert(openedSessionCount.getOpenSessionCount == 0) + + val badAuthorization = new String( + Base64.getEncoder.encode( + s"$ldapUser:".getBytes()), + "UTF-8") + response = webTarget.path("api/v1/sessions/count") + .request() + .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL.toString} $badAuthorization") + .get() + + assert(HttpServletResponse.SC_UNAUTHORIZED == response.getStatus) } }