We are attempting to use KeycloakOIDCFilter
as our Apache Spark UI filter. However, we are struggling to configure the KeycloakOIDCFilter
itself.
We have, in spark-defaults.conf:
spark.ui.filters=org.keycloak.adapters.servlet.KeycloakOIDCFilter
This is picked up successfully, and the Spark master logs show this filter being applied to all URL routes.
We have generated a client config file in the Keycloak Admin Console, which has spit out a keycloak-oidc.json
.
But how do we tell KeycloakOIDCFilter
about this information?
From the Spark docs
Filter parameters can also be specified in the configuration, by setting config entries of the form
spark.<class name of filter>.param.<param name>=<value>
For example:spark.ui.filters=com.test.filter1 spark.com.test.filter1.param.name1=foo spark.com.test.filter1.param.name2=bar
In our case that would seem to be:
spark.org.keycloak.adapters.servlet.KeycloakOIDCFilter.param.<name>=<value>
However, the KeycloakOIDCFilter Java class has only two constructors. One takes no parameters at all and one takes a KeycloakConfigResolver
.
The Keycloak Java servlet filter adapter docs only talk about web.xml
which isn't applicable in the case of configuring Spark.
So how can we properly configure/point to parameters for the KeycloakOIDCFilter
servlet filter?
Update: We've determined that spark.org.keycloak.adapters.servlet.KeycloakOIDCFilter.param.keycloak.config.file
can be used to point to a config file, but it appears that Spark does not use SessionManager, leading to a separate error that may or may not be resolvable.
I haven't tested the solution but, according to the Keycloak and Spark documentation you cited, and the source code of KeycloakOIDCFilter, assuming you are using a file in your filesystem, the following configuration could work:
spark.ui.filters=org.keycloak.adapters.servlet.KeycloakOIDCFilter
spark.org.keycloak.adapters.servlet.KeycloakOIDCFilter.param.keycloak.config.file=/path/to/keycloak-oidc.json
Or this other one if your config is accesible as a web app resource, through getServletContext().getResourceAsStream(...)
, instead of a file:
spark.ui.filters=org.keycloak.adapters.servlet.KeycloakOIDCFilter
spark.org.keycloak.adapters.servlet.KeycloakOIDCFilter.param.keycloak.config.path=/WEB-INF/keycloak-oidc.json
Please, note that they indicate that filters parameters can also be specified in the configuration: afaik, it doesn't mean that the filter should have any special constructor or something similar.
This configuration is performed by the addFilters
:
/**
* Add filters, if any, to the given ServletContextHandlers. Always adds a filter at the end
* of the chain to perform security-related functions.
*/
private def addFilters(handler: ServletContextHandler, securityMgr: SecurityManager): Unit = {
conf.get(UI_FILTERS).foreach { filter =>
logInfo(s"Adding filter to ${handler.getContextPath()}: $filter")
val oldParams = conf.getOption(s"spark.$filter.params").toSeq
.flatMap(Utils.stringToSeq)
.flatMap { param =>
val parts = param.split("=")
if (parts.length == 2) Some(parts(0) -> parts(1)) else None
}
.toMap
val newParams = conf.getAllWithPrefix(s"spark.$filter.param.").toMap
JettyUtils.addFilter(handler, filter, oldParams ++ newParams)
}
and addFilter:
def addFilter(
handler: ServletContextHandler,
filter: String,
params: Map[String, String]): Unit = {
val holder = new FilterHolder()
holder.setClassName(filter)
params.foreach { case (k, v) => holder.setInitParameter(k, v) }
handler.addFilter(holder, "/*", EnumSet.allOf(classOf[DispatcherType]))
}
methods in the JettyUtils
class in the source code of Spark UI.