This is an automated email from the ASF dual-hosted git repository.
chengpan pushed a commit to branch branch-1.8
in repository https://gitbox.apache.org/repos/asf/kyuubi.git
The following commit(s) were added to refs/heads/branch-1.8 by this push:
new 67493cf13 [KYUUBI #5568] Check administrator only when security is
enabled
67493cf13 is described below
commit 67493cf130504ab94165490690a05b49eb5f5886
Author: Cheng Pan <[email protected]>
AuthorDate: Thu Nov 2 10:09:01 2023 +0800
[KYUUBI #5568] Check administrator only when security is enabled
### _Why are the changes needed?_
This PR aims to improve out-of-box UX.
Currently, administrators' checking is enforced even when security is
disabled, and we are going to deliver the preview of Web UI in 1.8, it may
confuse users who use the default configuration to launch Kyuubi sever and
explore Kyuubi WebUI (because the user "anonymous" can not visit all users'
sessions/engines without being configured as an administrator).
### _How was this patch tested?_
- [ ] Add some test cases that check the changes thoroughly including
negative and positive cases if possible
- [ ] Add screenshots for manual tests if appropriate
- [x] [Run
test](https://kyuubi.readthedocs.io/en/master/contributing/code/testing.html#running-tests)
locally before make a pull request
### _Was this patch authored or co-authored using generative AI tooling?_
No
Closes #5568 from pan3793/admin.
Closes #5568
2eccf659f [Cheng Pan] code dedup and fix test
76f4fd4a3 [Cheng Pan] fix test
d7ca94650 [Cheng Pan] Check administrator only when security is enabled
Authored-by: Cheng Pan <[email protected]>
Signed-off-by: Cheng Pan <[email protected]>
---
docs/configuration/settings.md | 32 ++---
.../org/apache/kyuubi/config/KyuubiConf.scala | 4 +-
.../KyuubiAuthenticationFactory.scala | 44 ++-----
.../kyuubi/server/KyuubiRestFrontendService.scala | 16 ++-
.../kyuubi/server/KyuubiTHttpFrontendService.scala | 5 +-
.../kyuubi/server/api/v1/AdminResource.scala | 36 +++---
.../kyuubi/server/api/v1/BatchesResource.scala | 4 +-
.../kyuubi/server/http/ThriftHttpServlet.scala | 4 +-
.../http/authentication/AuthenticationFilter.scala | 2 +-
.../authentication/AuthenticationHandler.scala | 19 +--
.../BasicAuthenticationHandler.scala | 4 +-
.../KerberosAuthenticationHandler.scala | 8 +-
.../KyuubiInternalAuthenticationHandler.scala | 8 +-
.../kyuubi/server/http/util/HttpAuthUtils.scala | 22 +++-
.../operation/KyuubiRestAuthenticationSuite.scala | 19 +--
.../kyuubi/server/api/v1/AdminResourceSuite.scala | 139 ++++++++++-----------
.../server/api/v1/BatchesResourceSuite.scala | 66 +++++++---
.../server/api/v1/SessionsResourceSuite.scala | 9 +-
18 files changed, 221 insertions(+), 220 deletions(-)
diff --git a/docs/configuration/settings.md b/docs/configuration/settings.md
index 3ef24d51d..4acf39551 100644
--- a/docs/configuration/settings.md
+++ b/docs/configuration/settings.md
@@ -387,22 +387,22 @@ You can configure the Kyuubi properties in
`$KYUUBI_HOME/conf/kyuubi-defaults.co
### Server
-| Key | Default
|
Meaning
| Type | Since |
-|----------------------------------------------------------|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-------|
-| kyuubi.server.administrators
|| Comma-separated list of Kyuubi service administrators. We use this config to
grant admin permission to any service accounts.
| set | 1.8.0 |
-| kyuubi.server.info.provider | ENGINE
| The server information provider name, some clients may rely on this
information to check the server compatibilities and functionalities.
<li>SERVER: Return Kyuubi server information.</li> <li>ENGINE: Return Kyuubi
engine information.</li> | string | 1.6.1 |
-| kyuubi.server.limit.batch.connections.per.ipaddress | <undefined>
| Maximum kyuubi server batch connections per ipaddress. Any user exceeding
this limit will not be allowed to connect.
| int | 1.7.0 |
-| kyuubi.server.limit.batch.connections.per.user | <undefined>
| Maximum kyuubi server batch connections per user. Any user exceeding this
limit will not be allowed to connect.
| int | 1.7.0 |
-| kyuubi.server.limit.batch.connections.per.user.ipaddress | <undefined>
| Maximum kyuubi server batch connections per user:ipaddress combination. Any
user-ipaddress exceeding this limit will not be allowed to connect.
| int | 1.7.0 |
-| kyuubi.server.limit.client.fetch.max.rows | <undefined>
| Max rows limit for getting result row set operation. If the max rows
specified by client-side is larger than the limit, request will fail directly.
| int | 1.8.0 |
-| kyuubi.server.limit.connections.per.ipaddress | <undefined>
| Maximum kyuubi server connections per ipaddress. Any user exceeding this
limit will not be allowed to connect.
| int | 1.6.0 |
-| kyuubi.server.limit.connections.per.user | <undefined>
| Maximum kyuubi server connections per user. Any user exceeding this limit
will not be allowed to connect.
| int | 1.6.0 |
-| kyuubi.server.limit.connections.per.user.ipaddress | <undefined>
| Maximum kyuubi server connections per user:ipaddress combination. Any
user-ipaddress exceeding this limit will not be allowed to connect.
| int | 1.6.0 |
-| kyuubi.server.limit.connections.user.deny.list
|| The user in the deny list will be denied to connect to kyuubi server, if the
user has configured both user.unlimited.list and user.deny.list, the priority
of the latter is higher.
| set | 1.8.0 |
-| kyuubi.server.limit.connections.user.unlimited.list
|| The maximum connections of the user in the white list will not be limited.
| set | 1.7.0 |
-| kyuubi.server.name | <undefined>
| The name of Kyuubi Server.
| string | 1.5.0 |
-| kyuubi.server.periodicGC.interval | PT30M
| How often to trigger a garbage collection.
| duration | 1.7.0 |
-| kyuubi.server.redaction.regex | <undefined>
| Regex to decide which Kyuubi contain sensitive information. When this regex
matches a property key or value, the value is redacted from the various logs.
|| 1.6.0 |
+| Key | Default
|
Meaning
| Type | Since |
+|----------------------------------------------------------|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------|-------|
+| kyuubi.server.administrators
|| Comma-separated list of Kyuubi service administrators. We use this config to
grant admin permission to any service accounts when security mechanism is
enabled. Note, when kyuubi.authentication is configured to NOSASL or NONE,
everyone is treated as administrator. | set | 1.8.0 |
+| kyuubi.server.info.provider | ENGINE
| The server information provider name, some clients may rely on this
information to check the server compatibilities and functionalities.
<li>SERVER: Return Kyuubi server information.</li> <li>ENGINE: Return Kyuubi
engine information.</li> | string | 1.6.1 |
+| kyuubi.server.limit.batch.connections.per.ipaddress | <undefined>
| Maximum kyuubi server batch connections per ipaddress. Any user exceeding
this limit will not be allowed to connect.
| int | 1.7.0 |
+| kyuubi.server.limit.batch.connections.per.user | <undefined>
| Maximum kyuubi server batch connections per user. Any user exceeding this
limit will not be allowed to connect.
| int | 1.7.0 |
+| kyuubi.server.limit.batch.connections.per.user.ipaddress | <undefined>
| Maximum kyuubi server batch connections per user:ipaddress combination. Any
user-ipaddress exceeding this limit will not be allowed to connect.
| int | 1.7.0 |
+| kyuubi.server.limit.client.fetch.max.rows | <undefined>
| Max rows limit for getting result row set operation. If the max rows
specified by client-side is larger than the limit, request will fail directly.
| int | 1.8.0 |
+| kyuubi.server.limit.connections.per.ipaddress | <undefined>
| Maximum kyuubi server connections per ipaddress. Any user exceeding this
limit will not be allowed to connect.
| int | 1.6.0 |
+| kyuubi.server.limit.connections.per.user | <undefined>
| Maximum kyuubi server connections per user. Any user exceeding this limit
will not be allowed to connect.
| int | 1.6.0 |
+| kyuubi.server.limit.connections.per.user.ipaddress | <undefined>
| Maximum kyuubi server connections per user:ipaddress combination. Any
user-ipaddress exceeding this limit will not be allowed to connect.
| int | 1.6.0 |
+| kyuubi.server.limit.connections.user.deny.list
|| The user in the deny list will be denied to connect to kyuubi server, if the
user has configured both user.unlimited.list and user.deny.list, the priority
of the latter is higher.
| set | 1.8.0 |
+| kyuubi.server.limit.connections.user.unlimited.list
|| The maximum connections of the user in the white list will not be limited.
| set | 1.7.0 |
+| kyuubi.server.name | <undefined>
| The name of Kyuubi Server.
| string | 1.5.0 |
+| kyuubi.server.periodicGC.interval | PT30M
| How often to trigger a garbage collection.
| duration | 1.7.0 |
+| kyuubi.server.redaction.regex | <undefined>
| Regex to decide which Kyuubi contain sensitive information. When this regex
matches a property key or value, the value is redacted from the various logs.
|| 1.6.0 |
### Session
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 8f7e4bb4e..bd041425b 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
@@ -2709,7 +2709,9 @@ object KyuubiConf {
val SERVER_ADMINISTRATORS: ConfigEntry[Set[String]] =
buildConf("kyuubi.server.administrators")
.doc("Comma-separated list of Kyuubi service administrators. " +
- "We use this config to grant admin permission to any service
accounts.")
+ "We use this config to grant admin permission to any service accounts
when " +
+ s"security mechanism is enabled. Note, when
${AUTHENTICATION_METHOD.key} is " +
+ "configured to NOSASL or NONE, everyone is treated as administrator.")
.version("1.8.0")
.serverOnly
.stringConf
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 1b62f6030..2f56f3e4c 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
@@ -37,10 +37,9 @@ import org.apache.kyuubi.service.authentication.AuthTypes._
class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer: Boolean = true)
extends Logging {
- private val authTypes =
conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName)
- private val none = authTypes.contains(NONE)
- private val noSasl = authTypes == Set(NOSASL)
- private val kerberosEnabled = authTypes.contains(KERBEROS)
+ val authTypes: Set[AuthType] =
conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName)
+ val noSaslEnabled: Boolean = authTypes == Set(NOSASL)
+ val kerberosEnabled: Boolean = authTypes.contains(KERBEROS)
private val plainAuthTypeOpt = authTypes.filterNot(_.equals(KERBEROS))
.filterNot(_.equals(NOSASL)).headOption
@@ -71,7 +70,7 @@ class KyuubiAuthenticationFactory(conf: KyuubiConf, isServer:
Boolean = true) ex
}
def getTTransportFactory: TTransportFactory = {
- if (noSasl) {
+ if (noSaslEnabled) {
new TTransportFactory()
} else {
var transportFactory: TSaslServerTransport.Factory = null
@@ -119,33 +118,8 @@ class KyuubiAuthenticationFactory(conf: KyuubiConf,
isServer: Boolean = true) ex
hadoopAuthServer.map(_.getRemoteAddress).map(_.getHostAddress)
.orElse(Option(TSetIpAddressProcessor.getUserIpAddress))
}
-
- def isNoSaslEnabled: Boolean = {
- noSasl
- }
-
- def isKerberosEnabled: Boolean = {
- kerberosEnabled
- }
-
- def isPlainAuthEnabled: Boolean = {
- plainAuthTypeOpt.isDefined
- }
-
- def isNoneEnabled: Boolean = {
- none
- }
-
- def getValidPasswordAuthMethod: AuthMethod = {
- 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")
- }
}
-object KyuubiAuthenticationFactory {
+object KyuubiAuthenticationFactory extends Logging {
val HS2_PROXY_USER = "hive.server2.proxy.user"
@throws[KyuubiSQLException]
@@ -177,4 +151,12 @@ object KyuubiAuthenticationFactory {
e)
}
}
+
+ def getValidPasswordAuthMethod(authTypes: Set[AuthType]): AuthMethod = {
+ if (authTypes.contains(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")
+ }
}
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala
index c5d44213c..f7a09ee25 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiRestFrontendService.scala
@@ -35,7 +35,8 @@ import org.apache.kyuubi.server.api.v1.ApiRootResource
import org.apache.kyuubi.server.http.authentication.{AuthenticationFilter,
KyuubiHttpAuthenticationFactory}
import org.apache.kyuubi.server.ui.{JettyServer, JettyUtils}
import org.apache.kyuubi.service.{AbstractFrontendService, Serverable,
Service, ServiceUtils}
-import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory
+import org.apache.kyuubi.service.authentication.{AuthTypes,
KyuubiAuthenticationFactory}
+import org.apache.kyuubi.service.authentication.AuthTypes.NONE
import org.apache.kyuubi.session.{KyuubiSessionManager, SessionHandle}
import org.apache.kyuubi.util.ThreadUtils
@@ -70,6 +71,17 @@ class KyuubiRestFrontendService(override val serverable:
Serverable)
private lazy val port: Int = conf.get(FRONTEND_REST_BIND_PORT)
+ private lazy val securityEnabled = {
+ val authTypes = conf.get(AUTHENTICATION_METHOD).map(AuthTypes.withName)
+ KyuubiAuthenticationFactory.getValidPasswordAuthMethod(authTypes) != NONE
+ }
+
+ private lazy val administrators: Set[String] =
+ conf.get(KyuubiConf.SERVER_ADMINISTRATORS) + Utils.currentUser
+
+ def isAdministrator(userName: String): Boolean =
+ if (securityEnabled) administrators.contains(userName) else true
+
override def initialize(conf: KyuubiConf): Unit = synchronized {
this.conf = conf
server = JettyServer(
@@ -240,7 +252,7 @@ class KyuubiRestFrontendService(override val serverable:
Serverable)
realUser
} else {
sessionConf.get(KyuubiAuthenticationFactory.HS2_PROXY_USER).map {
proxyUser =>
- if (!getConf.get(KyuubiConf.SERVER_ADMINISTRATORS).contains(realUser))
{
+ if (!isAdministrator(realUser)) {
KyuubiAuthenticationFactory.verifyProxyAccess(realUser, proxyUser,
ipAddress, hadoopConf)
}
proxyUser
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
index 79351118c..c32345695 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/KyuubiTHttpFrontendService.scala
@@ -43,6 +43,7 @@ import org.apache.kyuubi.metrics.MetricsSystem
import org.apache.kyuubi.server.http.ThriftHttpServlet
import org.apache.kyuubi.server.http.util.SessionManager
import org.apache.kyuubi.service.{Serverable, Service, ServiceUtils,
TFrontendService}
+import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory
import org.apache.kyuubi.util.NamedThreadFactory
/**
@@ -74,9 +75,9 @@ final class KyuubiTHttpFrontendService(
*/
override def initialize(conf: KyuubiConf): Unit = synchronized {
this.conf = conf
- if (authFactory.isKerberosEnabled) {
+ if (authFactory.kerberosEnabled) {
try {
- authFactory.getValidPasswordAuthMethod
+
KyuubiAuthenticationFactory.getValidPasswordAuthMethod(authFactory.authTypes)
} catch {
case _: IllegalArgumentException =>
throw new AuthenticationException("Kerberos is not supported for
thrift http mode")
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala
index 3c6f2a197..b8ae73ea2 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/AdminResource.scala
@@ -29,7 +29,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
import org.apache.commons.lang3.StringUtils
-import org.apache.kyuubi.{KYUUBI_VERSION, Logging, Utils}
+import org.apache.kyuubi.{KYUUBI_VERSION, Logging}
import org.apache.kyuubi.client.api.v1.dto._
import org.apache.kyuubi.config.KyuubiConf
import org.apache.kyuubi.config.KyuubiConf._
@@ -45,8 +45,6 @@ import
org.apache.kyuubi.shaded.zookeeper.KeeperException.NoNodeException
@Tag(name = "Admin")
@Produces(Array(MediaType.APPLICATION_JSON))
private[v1] class AdminResource extends ApiRequestContext with Logging {
- private lazy val administrators =
fe.getConf.get(KyuubiConf.SERVER_ADMINISTRATORS) +
- Utils.currentUser
@ApiResponse(
responseCode = "200",
@@ -59,7 +57,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Receive refresh Kyuubi server hadoop conf request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to refresh the Kyuubi server hadoop conf")
}
@@ -78,7 +76,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Receive refresh user defaults conf request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to refresh the user defaults conf")
}
@@ -97,7 +95,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Receive refresh kubernetes conf request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to refresh the kubernetes conf")
}
@@ -116,7 +114,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Receive refresh unlimited users request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to refresh the unlimited users")
}
@@ -135,7 +133,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Receive refresh deny users request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to refresh the deny users")
}
@@ -156,7 +154,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received listing all live sessions request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to list all live sessions")
}
@@ -178,7 +176,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received closing a session request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to close the session $sessionHandleStr")
}
@@ -202,7 +200,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received listing all of the active operations request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to list all the operations")
}
@@ -229,7 +227,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received close an operation request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to close the operation $operationHandleStr")
}
@@ -249,7 +247,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
@QueryParam("sharelevel") shareLevel: String,
@QueryParam("subdomain") subdomain: String,
@QueryParam("hive.server2.proxy.user") hs2ProxyUser: String): Response =
{
- val userName = if (isAdministrator(fe.getRealUser())) {
+ val userName = if (fe.isAdministrator(fe.getRealUser())) {
Option(hs2ProxyUser).getOrElse(fe.getRealUser())
} else {
fe.getSessionUser(hs2ProxyUser)
@@ -292,7 +290,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received list all kyuubi engine request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to list all kyuubi engine")
}
@@ -334,7 +332,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
}
return engines.toSeq
}
- val userName = if (isAdministrator(fe.getRealUser())) {
+ val userName = if (fe.isAdministrator(fe.getRealUser())) {
Option(hs2ProxyUser).getOrElse(fe.getRealUser())
} else {
fe.getSessionUser(hs2ProxyUser)
@@ -394,7 +392,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received list all live kyuubi servers request from
$userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to list all live kyuubi servers")
}
@@ -466,7 +464,7 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
val userName = fe.getSessionUser(Map.empty[String, String])
val ipAddress = fe.getIpAddress
info(s"Received counting batches request from $userName/$ipAddress")
- if (!isAdministrator(userName)) {
+ if (!fe.isAdministrator(userName)) {
throw new NotAllowedException(
s"$userName is not allowed to count the batches")
}
@@ -475,8 +473,4 @@ private[v1] class AdminResource extends ApiRequestContext
with Logging {
.getOrElse(0)
new Count(batchCount)
}
-
- private def isAdministrator(userName: String): Boolean = {
- administrators.contains(userName)
- }
}
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/BatchesResource.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/BatchesResource.scala
index bc6a177e4..5e32016bf 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/BatchesResource.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/api/v1/BatchesResource.scala
@@ -219,6 +219,8 @@ private[v1] class BatchesResource extends ApiRequestContext
with Logging {
}
request.setBatchType(request.getBatchType.toUpperCase(Locale.ROOT))
+ val userName = fe.getSessionUser(request.getConf.asScala.toMap)
+ val ipAddress = fe.getIpAddress
val userProvidedBatchId = request.getConf.asScala.get(KYUUBI_BATCH_ID_KEY)
userProvidedBatchId.foreach { batchId =>
try UUID.fromString(batchId)
@@ -234,8 +236,6 @@ private[v1] class BatchesResource extends ApiRequestContext
with Logging {
case Some(batch) =>
markDuplicated(batch)
case None =>
- val userName = fe.getSessionUser(request.getConf.asScala.toMap)
- val ipAddress = fe.getIpAddress
val batchId = userProvidedBatchId.getOrElse(UUID.randomUUID().toString)
request.setConf(
(request.getConf.asScala ++ Map(
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala
index bb9f1553d..f65d3b274 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/ThriftHttpServlet.scala
@@ -35,8 +35,8 @@ import org.apache.kyuubi.Logging
import org.apache.kyuubi.config.KyuubiConf
import org.apache.kyuubi.config.KyuubiConf.FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER
import org.apache.kyuubi.server.http.authentication.AuthenticationFilter
-import
org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER
import org.apache.kyuubi.server.http.util.{CookieSigner, HttpAuthUtils,
SessionManager}
+import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER
import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory
class ThriftHttpServlet(
@@ -136,7 +136,7 @@ class ThriftHttpServlet(
} else SessionManager.setForwardedAddresses(List.empty[String])
// Generate new cookie and add it to the response
- if (requireNewCookie && !authFactory.isNoSaslEnabled) {
+ if (requireNewCookie && !authFactory.noSaslEnabled) {
val cookieToken = HttpAuthUtils.createCookieToken(clientUserName)
val hs2Cookie = createCookie(signer.signCookie(cookieToken))
if (isHttpOnlyCookie) response.setHeader("SET-COOKIE",
getHttpOnlyCookieHeader(hs2Cookie))
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 523d24907..15b387607 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
@@ -27,12 +27,12 @@ import scala.collection.mutable
import org.apache.kyuubi.Logging
import org.apache.kyuubi.config.KyuubiConf
import org.apache.kyuubi.config.KyuubiConf.{AUTHENTICATION_METHOD,
FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER}
+import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER
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 {
import AuthenticationFilter._
- import AuthenticationHandler._
import AuthSchemes._
private[authentication] val authSchemeHandlers =
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationHandler.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationHandler.scala
index bf2cb5bbe..a0b3fb4ab 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationHandler.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/AuthenticationHandler.scala
@@ -20,13 +20,11 @@ package org.apache.kyuubi.server.http.authentication
import javax.security.sasl.AuthenticationException
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
-import org.apache.hadoop.security.authentication.server.HttpConstants
-
import org.apache.kyuubi.config.KyuubiConf
import org.apache.kyuubi.server.http.authentication.AuthSchemes.AuthScheme
+import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER
trait AuthenticationHandler {
- import AuthenticationHandler._
/**
* HTTP header prefix used during the authentication sequence.
@@ -103,23 +101,10 @@ trait AuthenticationHandler {
authorization = authorization.stripPrefix(":").trim
}
// Authorization header must have a payload
- if (authorization == null || authorization.isEmpty()) {
+ if (authorization == null || authorization.isEmpty) {
throw new AuthenticationException(
"Authorization header received from the client does not contain any
data.")
}
authorization
}
}
-
-object AuthenticationHandler {
-
- /**
- * HTTP header used by the SPNEGO server endpoint during an authentication
sequence.
- */
- final val WWW_AUTHENTICATE: String = HttpConstants.WWW_AUTHENTICATE_HEADER
-
- /**
- * HTTP header used by the client endpoint during an authentication sequence.
- */
- final val AUTHORIZATION_HEADER = "Authorization"
-}
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/BasicAuthenticationHandler.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/BasicAuthenticationHandler.scala
index 57ce2e60e..76560cabb 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/BasicAuthenticationHandler.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/BasicAuthenticationHandler.scala
@@ -24,12 +24,12 @@ 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.server.http.util.HttpAuthUtils.{AUTHORIZATION_HEADER,
WWW_AUTHENTICATE_HEADER}
import
org.apache.kyuubi.service.authentication.{AuthenticationProviderFactory,
AuthMethods}
import org.apache.kyuubi.service.authentication.AuthTypes._
class BasicAuthenticationHandler(basicAuthType: AuthType)
extends AuthenticationHandler with Logging {
- import AuthenticationHandler._
private var conf: KyuubiConf = _
private val allowAnonymous = basicAuthType == NOSASL || basicAuthType == NONE
@@ -75,7 +75,7 @@ class BasicAuthenticationHandler(basicAuthType: AuthType)
authUser =
creds.take(1).headOption.filterNot(_.isEmpty).getOrElse("anonymous")
} else {
if (creds.size < 2 || creds(0).trim.isEmpty || creds(1).trim.isEmpty) {
- response.setHeader(WWW_AUTHENTICATE, authScheme.toString)
+ response.setHeader(WWW_AUTHENTICATE_HEADER, authScheme.toString)
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
} else {
val Seq(user, password) = creds.toSeq.take(2)
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KerberosAuthenticationHandler.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KerberosAuthenticationHandler.scala
index 04603f30a..7220e3906 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KerberosAuthenticationHandler.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/authentication/KerberosAuthenticationHandler.scala
@@ -27,15 +27,15 @@ import javax.servlet.ServletException
import javax.servlet.http.{HttpServletRequest, HttpServletResponse}
import org.apache.hadoop.security.authentication.util.KerberosName
+import org.apache.hadoop.security.authentication.util.KerberosUtil._
import org.ietf.jgss.{GSSContext, GSSCredential, GSSManager, Oid}
import org.apache.kyuubi.Logging
import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.server.http.authentication.AuthSchemes.AuthScheme
+import org.apache.kyuubi.server.http.util.HttpAuthUtils.{NEGOTIATE,
WWW_AUTHENTICATE_HEADER}
class KerberosAuthenticationHandler extends AuthenticationHandler with Logging
{
- import AuthenticationHandler._
- import AuthSchemes._
- import KerberosUtil._
private var gssManager: GSSManager = _
private var conf: KyuubiConf = _
@@ -143,7 +143,7 @@ class KerberosAuthenticationHandler extends
AuthenticationHandler with Logging {
val serverToken = gssContext.acceptSecContext(clientToken, 0,
clientToken.length)
if (serverToken != null && serverToken.nonEmpty) {
val authenticate = Base64.getEncoder.encodeToString(serverToken)
- response.setHeader(WWW_AUTHENTICATE, s"$NEGOTIATE $authenticate")
+ response.setHeader(WWW_AUTHENTICATE_HEADER, s"$NEGOTIATE
$authenticate")
}
if (!gssContext.isEstablished) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
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
index 7af6389cc..d910f4a83 100644
---
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
@@ -17,17 +17,17 @@
package org.apache.kyuubi.server.http.authentication
-import java.nio.charset.Charset
+import java.nio.charset.StandardCharsets
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.server.http.util.HttpAuthUtils.WWW_AUTHENTICATE_HEADER
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
@@ -48,10 +48,10 @@ class KyuubiInternalAuthenticationHandler extends
AuthenticationHandler with Log
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(":")
+ val creds = new String(inputToken, StandardCharsets.UTF_8).split(":")
if (creds.size < 2 || creds(0).trim.isEmpty || creds(1).trim.isEmpty) {
- response.setHeader(WWW_AUTHENTICATE, authScheme.toString)
+ response.setHeader(WWW_AUTHENTICATE_HEADER, authScheme.toString)
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED)
} else {
val Seq(user, password) = creds.toSeq.take(2)
diff --git
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/util/HttpAuthUtils.scala
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/util/HttpAuthUtils.scala
index 7bb117476..e840a307c 100644
---
a/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/util/HttpAuthUtils.scala
+++
b/kyuubi-server/src/main/scala/org/apache/kyuubi/server/http/util/HttpAuthUtils.scala
@@ -17,19 +17,33 @@
package org.apache.kyuubi.server.http.util
+import java.nio.charset.StandardCharsets
import java.security.SecureRandom
import java.util
-import java.util.StringTokenizer
+import java.util.{Base64, StringTokenizer}
import scala.collection.mutable
import org.apache.kyuubi.Logging
object HttpAuthUtils extends Logging {
- val WWW_AUTHENTICATE = "WWW-Authenticate"
- val AUTHORIZATION = "Authorization"
- val BASIC = "Basic"
+ // HTTP header used by the server endpoint during an authentication sequence.
+ val WWW_AUTHENTICATE_HEADER = "WWW-Authenticate"
+ // HTTP header used by the client endpoint during an authentication sequence.
+ val AUTHORIZATION_HEADER = "Authorization"
+ // HTTP header prefix used by the SPNEGO client/server endpoints during an
+ // authentication sequence.
val NEGOTIATE = "Negotiate"
+ // HTTP header prefix used during the Basic authentication sequence.
+ val BASIC = "Basic"
+ // HTTP header prefix used during the Basic authentication sequence.
+ val DIGEST = "Digest"
+
+ // RFC 7617: The 'Basic' HTTP Authentication Scheme
+ def basicAuthorizationHeader(userId: String, password: String = "none"):
String =
+ "BASIC " + new String(
+ Base64.getEncoder.encode(s"$userId:$password".getBytes()),
+ StandardCharsets.UTF_8)
private val COOKIE_ATTR_SEPARATOR = "&"
private val COOKIE_CLIENT_USER_NAME = "cu"
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 089b756f5..260264b67 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,8 +29,8 @@ import org.apache.hadoop.security.UserGroupInformation
import org.apache.kyuubi.RestClientTestHelper
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.server.http.authentication.AuthSchemes
+import org.apache.kyuubi.server.http.util.HttpAuthUtils._
import org.apache.kyuubi.service.authentication.InternalSecurityAccessor
import org.apache.kyuubi.session.KyuubiSession
@@ -52,13 +52,10 @@ class KyuubiRestAuthenticationSuite extends
RestClientTestHelper {
}
test("test with LDAP authorization") {
- val encodeAuthorization = new String(
- Base64.getEncoder.encode(
- s"$ldapUser:$ldapUserPasswd".getBytes()),
- "UTF-8")
+
val response = webTarget.path("api/v1/sessions/count")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader(ldapUser,
ldapUserPasswd))
.get()
assert(HttpServletResponse.SC_OK == response.getStatus)
@@ -67,13 +64,9 @@ class KyuubiRestAuthenticationSuite extends
RestClientTestHelper {
}
test("test with CUSTOM authorization") {
- val encodeAuthorization = new String(
- Base64.getEncoder.encode(
- s"$customUser:$customPasswd".getBytes()),
- "UTF-8")
val response = webTarget.path("api/v1/sessions/count")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader(customUser,
customPasswd))
.get()
assert(HttpServletResponse.SC_FORBIDDEN == response.getStatus)
@@ -170,7 +163,7 @@ class KyuubiRestAuthenticationSuite extends
RestClientTestHelper {
"UTF-8")
var response = webTarget.path("api/v1/sessions/count")
.request()
- .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL.toString}
$encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL}
$encodeAuthorization")
.get()
assert(HttpServletResponse.SC_OK == response.getStatus)
@@ -183,7 +176,7 @@ class KyuubiRestAuthenticationSuite extends
RestClientTestHelper {
"UTF-8")
response = webTarget.path("api/v1/sessions/count")
.request()
- .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL.toString}
$badAuthorization")
+ .header(AUTHORIZATION_HEADER, s"${AuthSchemes.KYUUBI_INTERNAL}
$badAuthorization")
.get()
assert(HttpServletResponse.SC_UNAUTHORIZED == response.getStatus)
diff --git
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala
index ea87e3ea0..c570f3a72 100644
---
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala
+++
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/AdminResourceSuite.scala
@@ -17,9 +17,8 @@
package org.apache.kyuubi.server.api.v1
-import java.nio.charset.StandardCharsets
import java.time.Duration
-import java.util.{Base64, UUID}
+import java.util.UUID
import javax.ws.rs.client.Entity
import javax.ws.rs.core.{GenericType, MediaType}
@@ -31,8 +30,9 @@ import org.scalatest.time.SpanSugar.convertIntToGrainOfTime
import org.scalatestplus.mockito.MockitoSugar.mock
import org.apache.kyuubi.{KYUUBI_VERSION, KyuubiFunSuite,
RestFrontendTestHelper, Utils}
-import org.apache.kyuubi.client.api.v1.dto.{Engine, OperationData, ServerData,
SessionData, SessionHandle, SessionOpenRequest}
+import org.apache.kyuubi.client.api.v1.dto._
import org.apache.kyuubi.config.KyuubiConf
+import org.apache.kyuubi.config.KyuubiConf._
import
org.apache.kyuubi.config.KyuubiReservedKeys.KYUUBI_SESSION_CONNECTION_URL_KEY
import org.apache.kyuubi.engine.{ApplicationManagerInfo, ApplicationState,
EngineRef, KyuubiApplicationManager}
import org.apache.kyuubi.engine.EngineType.SPARK_SQL
@@ -42,22 +42,19 @@ import org.apache.kyuubi.ha.client.{DiscoveryPaths,
ServiceDiscovery}
import org.apache.kyuubi.ha.client.DiscoveryClientProvider.withDiscoveryClient
import org.apache.kyuubi.plugin.PluginLoader
import org.apache.kyuubi.server.KyuubiRestFrontendService
-import
org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER
+import org.apache.kyuubi.server.http.util.HttpAuthUtils
+import org.apache.kyuubi.server.http.util.HttpAuthUtils.AUTHORIZATION_HEADER
+import
org.apache.kyuubi.service.authentication.AnonymousAuthenticationProviderImpl
class AdminResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper {
private val engineMgr = new KyuubiApplicationManager()
override protected lazy val conf: KyuubiConf = KyuubiConf()
- .set(KyuubiConf.SERVER_ADMINISTRATORS, Set("admin001"))
- .set(KyuubiConf.ENGINE_IDLE_TIMEOUT, Duration.ofMinutes(3).toMillis)
-
- private val encodeAuthorization: String = {
- new String(
- Base64.getEncoder.encode(
- s"${Utils.currentUser}:".getBytes()),
- StandardCharsets.UTF_8)
- }
+ .set(AUTHENTICATION_METHOD, Set("CUSTOM"))
+ .set(AUTHENTICATION_CUSTOM_CLASS,
classOf[AnonymousAuthenticationProviderImpl].getName)
+ .set(SERVER_ADMINISTRATORS, Set("admin001"))
+ .set(ENGINE_IDLE_TIMEOUT, Duration.ofMinutes(3).toMillis)
override def beforeAll(): Unit = {
super.beforeAll()
@@ -74,70 +71,64 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
var response = webTarget.path("api/v1/admin/refresh/hadoop_conf")
.request()
.post(null)
- assert(405 == response.getStatus)
+ assert(response.getStatus === 401)
response = webTarget.path("api/v1/admin/refresh/hadoop_conf")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.post(null)
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
- val admin001AuthHeader = new String(
- Base64.getEncoder.encode("admin001".getBytes()),
- StandardCharsets.UTF_8)
response = webTarget.path("api/v1/admin/refresh/hadoop_conf")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $admin001AuthHeader")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader("admin001"))
.post(null)
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
- val admin002AuthHeader = new String(
- Base64.getEncoder.encode("admin002".getBytes()),
- StandardCharsets.UTF_8)
response = webTarget.path("api/v1/admin/refresh/hadoop_conf")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $admin002AuthHeader")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader("admin002"))
.post(null)
- assert(405 == response.getStatus)
+ assert(response.getStatus === 405)
}
test("refresh user defaults config of the kyuubi server") {
var response = webTarget.path("api/v1/admin/refresh/user_defaults_conf")
.request()
.post(null)
- assert(405 == response.getStatus)
+ assert(response.getStatus === 401)
response = webTarget.path("api/v1/admin/refresh/user_defaults_conf")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.post(null)
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
}
test("refresh unlimited users of the kyuubi server") {
var response = webTarget.path("api/v1/admin/refresh/unlimited_users")
.request()
.post(null)
- assert(405 == response.getStatus)
+ assert(response.getStatus === 401)
response = webTarget.path("api/v1/admin/refresh/unlimited_users")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.post(null)
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
}
test("refresh deny users of the kyuubi server") {
var response = webTarget.path("api/v1/admin/refresh/deny_users")
.request()
.post(null)
- assert(405 == response.getStatus)
+ assert(response.getStatus === 401)
response = webTarget.path("api/v1/admin/refresh/deny_users")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.post(null)
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
}
test("list/close sessions") {
@@ -145,13 +136,15 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
var response = webTarget.path("api/v1/sessions")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.post(Entity.entity(requestObj, MediaType.APPLICATION_JSON_TYPE))
+ assert(response.getStatus === 200)
// get session list
var response2 = webTarget.path("api/v1/admin/sessions").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
- assert(200 == response2.getStatus)
+ assert(response2.getStatus === 200)
val sessions1 = response2.readEntity(new GenericType[Seq[SessionData]]()
{})
assert(sessions1.nonEmpty)
assert(sessions1.head.getConf.get(KYUUBI_SESSION_CONNECTION_URL_KEY) ===
fe.connectionUrl)
@@ -159,13 +152,13 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
// close an opened session
val sessionHandle =
response.readEntity(classOf[SessionHandle]).getIdentifier
response =
webTarget.path(s"api/v1/admin/sessions/$sessionHandle").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.delete()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
// get session list again
response2 = webTarget.path("api/v1/admin/sessions").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
assert(200 == response2.getStatus)
val sessions2 = response2.readEntity(classOf[Seq[SessionData]])
@@ -205,26 +198,26 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
var response = webTarget.path("api/v1/admin/sessions")
.queryParam("users", "admin")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
var sessions = response.readEntity(classOf[Seq[SessionData]])
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
assert(sessions.size == 2)
response = webTarget.path("api/v1/admin/sessions")
.queryParam("users", "test_user_1,test_user_2")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
sessions = response.readEntity(classOf[Seq[SessionData]])
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
assert(sessions.size == 2)
// list operations
response = webTarget.path("api/v1/admin/operations")
.queryParam("users", "test_user_1,test_user_2")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
var operations = response.readEntity(classOf[Seq[OperationData]])
assert(operations.size == 2)
@@ -232,10 +225,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
response = webTarget.path("api/v1/admin/operations")
.queryParam("sessionHandle", sessionHandle.identifier)
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
operations = response.readEntity(classOf[Seq[OperationData]])
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
assert(operations.size == 1)
}
@@ -250,22 +243,22 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
// list operations
var response = webTarget.path("api/v1/admin/operations").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
var operations = response.readEntity(new GenericType[Seq[OperationData]]()
{})
assert(operations.nonEmpty)
assert(operations.map(op =>
op.getIdentifier).contains(operation.identifier.toString))
// close operation
response =
webTarget.path(s"api/v1/admin/operations/${operation.identifier}").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.delete()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
// list again
response = webTarget.path("api/v1/admin/operations").request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get()
operations = response.readEntity(new GenericType[Seq[OperationData]]() {})
assert(!operations.map(op =>
op.getIdentifier).contains(operation.identifier.toString))
@@ -297,10 +290,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
.queryParam("sharelevel", "USER")
.queryParam("type", "spark_sql")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.delete()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
assert(client.pathExists(engineSpace))
eventually(timeout(5.seconds), interval(100.milliseconds)) {
assert(client.getChildren(engineSpace).isEmpty, s"refId same with
$id?")
@@ -343,10 +336,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
.queryParam("sharelevel", "GROUP")
.queryParam("type", "spark_sql")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.delete()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
assert(client.pathExists(engineSpace))
eventually(timeout(5.seconds), interval(100.milliseconds)) {
assert(client.getChildren(engineSpace).isEmpty, s"refId same with
$id?")
@@ -387,10 +380,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
.queryParam("type", "spark_sql")
.queryParam("subdomain", id)
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.delete()
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
}
}
@@ -419,10 +412,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("type", "spark_sql")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val engines = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(engines.size == 1)
assert(engines(0).getEngineType == "SPARK_SQL")
@@ -465,10 +458,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("type", "spark_sql")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val engines = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(engines.size == 1)
assert(engines(0).getEngineType == "SPARK_SQL")
@@ -524,9 +517,9 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("type", "spark_sql")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val result = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(result.size == 2)
@@ -534,7 +527,7 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
.queryParam("type", "spark_sql")
.queryParam("subdomain", id1)
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
assert(200 == response1.getStatus)
val result1 = response1.readEntity(new GenericType[Seq[Engine]]() {})
@@ -562,10 +555,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/server")
.request()
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val result = response.readEntity(new GenericType[Seq[ServerData]]() {})
assert(result.size == 1)
val testServer = result.head
@@ -610,10 +603,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("all", "true")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val engines = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(engines.size == 1)
assert(engines(0).getEngineType == "SPARK_SQL")
@@ -657,10 +650,10 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("all", "true")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val engines = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(engines.size == 1)
assert(engines(0).getEngineType == "SPARK_SQL")
@@ -717,9 +710,9 @@ class AdminResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
val response = webTarget.path("api/v1/admin/engine")
.queryParam("all", "true")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER,
HttpAuthUtils.basicAuthorizationHeader(Utils.currentUser))
.get
- assert(200 == response.getStatus)
+ assert(response.getStatus === 200)
val result = response.readEntity(new GenericType[Seq[Engine]]() {})
assert(result.size == 2)
diff --git
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala
index 9ad51cfda..fda47d583 100644
---
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala
+++
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/BatchesResourceSuite.scala
@@ -19,7 +19,7 @@ package org.apache.kyuubi.server.api.v1
import java.net.InetAddress
import java.nio.file.Paths
-import java.util.{Base64, UUID}
+import java.util.UUID
import javax.ws.rs.client.Entity
import javax.ws.rs.core.{MediaType, Response}
@@ -43,9 +43,9 @@ import org.apache.kyuubi.metrics.{MetricsConstants,
MetricsSystem}
import org.apache.kyuubi.operation.{BatchJobSubmission, OperationState}
import org.apache.kyuubi.operation.OperationState.OperationState
import org.apache.kyuubi.server.{KyuubiBatchService, KyuubiRestFrontendService}
-import
org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER
+import
org.apache.kyuubi.server.http.util.HttpAuthUtils.{basicAuthorizationHeader,
AUTHORIZATION_HEADER}
import org.apache.kyuubi.server.metadata.api.{Metadata, MetadataFilter}
-import org.apache.kyuubi.service.authentication.KyuubiAuthenticationFactory
+import
org.apache.kyuubi.service.authentication.{AnonymousAuthenticationProviderImpl,
KyuubiAuthenticationFactory}
import org.apache.kyuubi.session.{KyuubiBatchSession, KyuubiSessionManager,
SessionHandle, SessionType}
class BatchesV1ResourceSuite extends BatchesResourceSuiteBase {
@@ -58,8 +58,8 @@ class BatchesV2ResourceSuite extends BatchesResourceSuiteBase
{
override def batchVersion: String = "2"
override def customConf: Map[String, String] = Map(
- KyuubiConf.METADATA_REQUEST_ASYNC_RETRY_ENABLED.key -> "false",
- KyuubiConf.BATCH_SUBMITTER_ENABLED.key -> "true")
+ METADATA_REQUEST_ASYNC_RETRY_ENABLED.key -> "false",
+ BATCH_SUBMITTER_ENABLED.key -> "true")
override def afterEach(): Unit = {
val sessionManager =
fe.be.sessionManager.asInstanceOf[KyuubiSessionManager]
@@ -82,11 +82,13 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
def customConf: Map[String, String]
override protected lazy val conf: KyuubiConf = {
+ val testResourceDir = Paths.get(sparkBatchTestResource.get).getParent
val kyuubiConf = KyuubiConf()
- .set(KyuubiConf.BATCH_IMPL_VERSION, batchVersion)
- .set(
- KyuubiConf.SESSION_LOCAL_DIR_ALLOW_LIST,
- Set(Paths.get(sparkBatchTestResource.get).getParent.toString))
+ .set(AUTHENTICATION_METHOD, Set("CUSTOM"))
+ .set(AUTHENTICATION_CUSTOM_CLASS,
classOf[AnonymousAuthenticationProviderImpl].getName)
+ .set(SERVER_ADMINISTRATORS, Set("admin"))
+ .set(BATCH_IMPL_VERSION, batchVersion)
+ .set(SESSION_LOCAL_DIR_ALLOW_LIST, Set(testResourceDir.toString))
customConf.foreach { case (k, v) => kyuubiConf.set(k, v) }
kyuubiConf
}
@@ -107,6 +109,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val response = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(requestObj, MediaType.APPLICATION_JSON_TYPE))
assert(response.getStatus === 200)
var batch = response.readEntity(classOf[Batch])
@@ -130,6 +133,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val proxyUserRequest = requestObj
val proxyUserResponse = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(proxyUserRequest, MediaType.APPLICATION_JSON_TYPE))
assert(proxyUserResponse.getStatus === 405)
var errorMessage = "Failed to validate proxy privilege of anonymous for
root"
@@ -137,6 +141,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
var getBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(getBatchResponse.getStatus === 200)
batch = getBatchResponse.readEntity(classOf[Batch])
@@ -161,6 +166,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// invalid batchId
getBatchResponse = webTarget.path(s"api/v1/batches/invalidBatchId")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(getBatchResponse.getStatus === 404)
@@ -172,6 +178,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "1")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
log = logResponse.readEntity(classOf[OperationLog])
assert(log.getRowCount === 1)
@@ -185,6 +192,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "-1")
.queryParam("size", "100")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
log = logResponse.readEntity(classOf[OperationLog])
if (log.getRowCount > 0) {
@@ -198,11 +206,9 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
}
// invalid user name
- val encodeAuthorization =
- new String(Base64.getEncoder.encode(batch.getId.getBytes()), "UTF-8")
var deleteBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader(batch.getId))
.delete()
assert(deleteBatchResponse.getStatus === 405)
errorMessage = s"${batch.getId} is not allowed to close the session belong
to anonymous"
@@ -211,12 +217,14 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// invalid batchId
deleteBatchResponse = webTarget.path(s"api/v1/batches/notValidUUID")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteBatchResponse.getStatus === 404)
// non-existed batch session
deleteBatchResponse =
webTarget.path(s"api/v1/batches/${UUID.randomUUID().toString}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteBatchResponse.getStatus === 404)
@@ -224,6 +232,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
deleteBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.queryParam("hive.server2.proxy.user", "invalidProxy")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteBatchResponse.getStatus === 405)
errorMessage = "Failed to validate proxy privilege of anonymous for
invalidProxy"
@@ -232,6 +241,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// check close batch session
deleteBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteBatchResponse.getStatus === 200)
val closeBatchResponse =
deleteBatchResponse.readEntity(classOf[CloseBatchResponse])
@@ -239,6 +249,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// check state after close batch session
getBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(getBatchResponse.getStatus === 200)
batch = getBatchResponse.readEntity(classOf[Batch])
@@ -252,6 +263,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// close the closed batch session
deleteBatchResponse = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteBatchResponse.getStatus === 200)
assert(!deleteBatchResponse.readEntity(classOf[CloseBatchResponse]).isSuccess)
@@ -267,6 +279,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val response = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(multipart, MediaType.MULTIPART_FORM_DATA))
assert(response.getStatus === 200)
val batch = response.readEntity(classOf[Batch])
@@ -289,6 +302,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
eventually(timeout(5.seconds), interval(200.millis)) {
val resp = webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
val batchState = resp.readEntity(classOf[Batch]).getState
assert(batchState === "PENDING" || batchState === "RUNNING")
@@ -296,6 +310,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
webTarget.path(s"api/v1/batches/${batch.getId}")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
eventually(timeout(5.seconds), interval(200.millis)) {
assert(KyuubiApplicationManager.uploadWorkDir.toFile.listFiles().isEmpty)
@@ -310,6 +325,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val resp1 = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(reqObj, MediaType.APPLICATION_JSON_TYPE))
assert(resp1.getStatus === 200)
val batch1 = resp1.readEntity(classOf[Batch])
@@ -317,6 +333,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val resp2 = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(reqObj, MediaType.APPLICATION_JSON_TYPE))
assert(resp2.getStatus === 200)
val batch2 = resp2.readEntity(classOf[Batch])
@@ -340,6 +357,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response.getStatus === 200)
@@ -394,6 +412,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response2.getStatus === 200)
@@ -406,6 +425,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "2")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response3.getStatus === 200)
@@ -418,6 +438,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "3")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response4.getStatus === 200)
@@ -429,6 +450,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "2")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response5.getStatus === 200)
@@ -441,6 +463,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "2")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response6.getStatus === 200)
@@ -455,6 +478,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "2")
.queryParam("size", "2")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response7.getStatus === 500)
}
@@ -485,6 +509,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
"resource is a required parameter")).foreach { case (req, msg) =>
val response = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(req, MediaType.APPLICATION_JSON_TYPE))
assert(response.getStatus === 500)
assert(response.readEntity(classOf[String]).contains(msg))
@@ -498,6 +523,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
"Invalid batchId: 3ea7ddbe-0c35-45da-85ad-3186770181a7")).foreach {
case (batchId, msg) =>
val response = webTarget.path(s"api/v1/batches/$batchId")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get
assert(response.getStatus === 404)
assert(response.readEntity(classOf[String]).contains(msg))
@@ -614,6 +640,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "1")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
batchVersion match {
case "1" =>
@@ -632,6 +659,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "1")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(logResponse.getStatus === 404)
assert(logResponse.readEntity(classOf[String]).contains("Invalid batchId"))
@@ -646,6 +674,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
.queryParam("from", "0")
.queryParam("size", "1")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(logResponse.getStatus === 500)
assert(logResponse.readEntity(classOf[String]).contains(
@@ -668,13 +697,10 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
engineType = "SPARK")
sessionManager.insertMetadata(metadata)
- val encodeAuthorization =
- new String(Base64.getEncoder.encode("kyuubi".getBytes()), "UTF-8")
-
// delete the batch in the same kyuubi instance but not found in-memory
var deleteResp = webTarget.path(s"api/v1/batches/${metadata.identifier}")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("kyuubi"))
.delete()
assert(deleteResp.getStatus === 200)
assert(!deleteResp.readEntity(classOf[CloseBatchResponse]).isSuccess)
@@ -682,7 +708,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// delete batch that is not existing
deleteResp = webTarget.path(s"api/v1/batches/${UUID.randomUUID.toString}")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("kyuubi"))
.delete()
assert(deleteResp.getStatus === 404)
assert(deleteResp.readEntity(classOf[String]).contains("Invalid batchId:"))
@@ -695,7 +721,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
// delete batch that need make redirection
deleteResp = webTarget.path(s"api/v1/batches/${metadata2.identifier}")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(AUTHORIZATION_HEADER, s"BASIC $encodeAuthorization")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("kyuubi"))
.delete()
assert(deleteResp.getStatus === 200)
assert(deleteResp.readEntity(classOf[CloseBatchResponse]).getMsg.contains(
@@ -710,6 +736,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val response = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.header(conf.get(FRONTEND_PROXY_HTTP_CLIENT_IP_HEADER), realClientIp)
.post(Entity.entity(requestObj, MediaType.APPLICATION_JSON_TYPE))
assert(response.getStatus === 200)
@@ -740,6 +767,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
eventually(timeout(10.seconds)) {
val response = webTarget.path("api/v1/batches")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.post(Entity.entity(requestObj, MediaType.APPLICATION_JSON_TYPE))
assert(response.getStatus === 200)
val batch = response.readEntity(classOf[Batch])
@@ -755,6 +783,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val deleteResp = webTarget.path(s"api/v1/batches/$batchId")
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.delete()
assert(deleteResp.getStatus === 200)
@@ -816,6 +845,7 @@ abstract class BatchesResourceSuiteBase extends
KyuubiFunSuite
val response = webTarget.path("api/v1/batches")
.queryParam("batchName", uniqueName)
.request(MediaType.APPLICATION_JSON_TYPE)
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("anonymous"))
.get()
assert(response.getStatus == 200)
diff --git
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala
index b58e87bc8..b993789ba 100644
---
a/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala
+++
b/kyuubi-server/src/test/scala/org/apache/kyuubi/server/api/v1/SessionsResourceSuite.scala
@@ -35,7 +35,7 @@ import
org.apache.kyuubi.config.KyuubiReservedKeys.KYUUBI_SESSION_CONNECTION_URL
import org.apache.kyuubi.engine.ShareLevel
import org.apache.kyuubi.metrics.{MetricsConstants, MetricsSystem}
import org.apache.kyuubi.operation.OperationHandle
-import
org.apache.kyuubi.server.http.authentication.AuthenticationHandler.AUTHORIZATION_HEADER
+import
org.apache.kyuubi.server.http.util.HttpAuthUtils.{basicAuthorizationHeader,
AUTHORIZATION_HEADER}
import org.apache.kyuubi.session.SessionType
class SessionsResourceSuite extends KyuubiFunSuite with RestFrontendTestHelper
{
@@ -106,14 +106,9 @@ class SessionsResourceSuite extends KyuubiFunSuite with
RestFrontendTestHelper {
test("get session event") {
val sessionOpenRequest = new SessionOpenRequest(Map("testConfig" ->
"testValue").asJava)
-
- val user = "kyuubi".getBytes()
-
val sessionOpenResp = webTarget.path("api/v1/sessions")
.request(MediaType.APPLICATION_JSON_TYPE)
- .header(
- AUTHORIZATION_HEADER,
- s"Basic ${new String(Base64.getEncoder().encode(user),
StandardCharsets.UTF_8)}")
+ .header(AUTHORIZATION_HEADER, basicAuthorizationHeader("kyuubi"))
.post(Entity.entity(sessionOpenRequest, MediaType.APPLICATION_JSON_TYPE))
val sessionHandle =
sessionOpenResp.readEntity(classOf[SessionHandle]).getIdentifier