Apache Superset Security Implementing Using Keycloak
Introduction
What is Apache Superset?
Apache Superset is a modern, enterprise-ready business intelligence web application. It is fast, lightweight, intuitive, and loaded with options that make it easy for users of all skill sets to explore and visualize their data, from simple pie charts to highly detailed deck.gl geospatial charts.
Superset provides:
- An intuitive interface for visualizing datasets and crafting interactive dashboards
- A wide array of beautiful visualizations to showcase your data
- Code-free visualization builder to extract and present datasets
- A world-class SQL IDE for preparing data for visualization, including a rich metadata browser
- A lightweight semantic layer that empowers data analysts to quickly define custom dimensions and metrics
- Out-of-the-box support for most SQL-speaking databases
- Seamless, in-memory asynchronous caching and queries
- An extensible security model that allows the configuration of very intricate rules on who can access which product features and datasets.
- Integration with major authentication backends (database, OpenID, LDAP, OAuth, REMOTE_USER, etc)
- The ability to add custom visualization plugins
- An API for programmatic customization
- A cloud-native architecture designed from the ground up for scale
What is Keycloak?
Keycloak is an open-source software product to allow single sign-on with Identity and Access Management aimed at modern applications and services.
Keycloak provides:
- single sign-on and sign-out, with possible integration with Kerberos (LDAP or Active Directory),
- support for OpenID Connect and SAML 2.0,
- log in via social media,
- user account management via both the web console and REST API,
- fine-grained authorization for different services.
About My Environments
Keycloak — Installed on a Docker environment
(Keyclaok installation guide on ubuntu)
Superset — Installed on windows local machine
(Superset installation guide on windows)
Step By Step Integration Process
For this integration, I’m using flask-oidc plugin. Because there was a small mismatch in the Keyclaok side and superset side.
Keycloak provided OpenId-Connect and Superset provided Flask-OpenID which is only supported for OpenID 2.x.
So When I’m looking for a solution to that I found a perfect answer from the thijsfranck. Huge thanks to him as well.
File Modifications inside superset
Extend the Security Manager
Firstly, you will want to make sure that the flask stops using flask-openid ad starts using flask-oidc instead. To do so, you will need to create your own security manager that configures flask-oidc as its authentication provider.
- I’ve created the customSecurity.py file right next to the config.py file (venv\Lib\site-packages\superset), and it looks like this:
from flask import redirect, request
from flask_appbuilder.security.manager import AUTH_OID
from superset.security import SupersetSecurityManager
from flask_oidc import OpenIDConnect
from flask_appbuilder.security.views import AuthOIDView
from flask_login import login_user
from urllib.parse import quote
from flask_appbuilder.views import ModelView, SimpleFormView, expose
import loggingclass AuthOIDCView(AuthOIDView):@expose('/login/', methods=['GET', 'POST'])
def login(self, flag=True):
sm = self.appbuilder.sm
oidc = sm.oid @self.appbuilder.sm.oid.require_login
def handle_login():
user = sm.auth_user_oid(oidc.user_getfield('email')) if user is None:
info = oidc.user_getinfo(['preferred_username',
'given_name', 'family_name', 'email']) user = sm.add_user(info.get('preferred_username'),
info.get('given_name'), info.get('family_name'),
info.get('email'), sm.find_role('Gamma')) login_user(user, remember=False)
return redirect(self.appbuilder.get_url_for_index) return handle_login()@expose('/logout/', methods=['GET', 'POST'])
def logout(self): oidc = self.appbuilder.sm.oid oidc.logout()
super(AuthOIDCView, self).logout()
redirect_url = request.url_root.strip('/') +
self.appbuilder.get_url_for_login return redirect(oidc.client_secrets.get('issuer') +
'/protocol/openid-connect/logout?redirect_uri=' +
quote(redirect_url))class OIDCSecurityManager(SupersetSecurityManager):
authoidview = AuthOIDCView
def __init__(self,appbuilder):
super(OIDCSecurityManager, self).__init__(appbuilder)
if self.auth_type == AUTH_OID:
self.oid = OpenIDConnect(self.appbuilder.get_app)
2. Then I created a client_secret.json
file, also inside the superset directory.
{
"web": {
"realm_public_key": "<PUBLIC_KEY>",
"issuer": "https://<DOMAIN>/auth/realms/demo",
"auth_uri":"https://<DOMAIN>/auth/realms/demo/protocol/openid-
connect/auth",
"client_id": "local",
"client_secret": "<CLIENT_SECRET>",
"verify_ssl_server": false
"redirect_urls": [
"http://localhost:8088/oidc/callback"
],
"userinfo_uri":"https://<DOMAIN>/auth/realms/demo/
protocol/openid-connect/userinfo",
"token_uri":"https://<DOMAIN>/auth/realms/demo/
protocol/openid-connect/token",
"token_introspection_uri":"https://<DOMAIN>/auth/realms/
demo/protocol/openid-
connect/token/introspect"
}
}
3. Then I’ve changed the config.py file to include the following lines:
AUTH_TYPE = AUTH_OIDOIDC_CLIENT_SECRETS='client_secret.json'OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
OIDC_CLOCK_SKEW = 560
OIDC_VALID_ISSUERS = 'https://<Domain>:<Port>/auth/realms/<Realm>'
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManagerOIDC_INTROSPECTION_AUTH_METHOD = 'client_secret_post'
OIDC_TOKEN_TYPE_HINT = 'access_token'
These are the 3 possible changes you have to do to connect keycloak to superset.
Code Explanation for Extend Security Manager (customSecurity.py)
A very basic explanation to this login scenario is,
- Checking the keycloak user is already existing inside the superset database
- If the user exists, return the user to the login handler
- If the user does not exist, create a user from the attributes that are returned from keycloak. (This part is important because if you didn't add the required values on keycloak side, this save function will occur an error.)
Issues I faced in my implementation
I did all those changes to superset side and it occurred an error called self-signed certificate failed. That is because my keycloak instance is inside a outside server. For testing purposes, I temporarily disabled the SSL functionality from superset. So I will mention these steps as well.
Inside the site-packages
there’s a package called oauth2client
. Inside that package, you can find a file named transport.py
Go to line number 73 inside that file and you can find a code line like this,
return httplib2.Http(*args, **kwargs)
replace that line with this,
return httplib2.Http(*args, **kwargs, disable_ssl_certificate_validation=True)
Inside the site-packages
there’s another package called flask_oidc
. Inside that package, you can find a file named _init_.py
Inside that file, there’re three places that are using httplib2.Http()
function.
Line no 309 / 445 / 892.
In that all three places, update httplib2.Http()
function like below.
httplib2.Http(disable_ssl_certificate_validation=True)
Now that your connection would work without any issue. But please be in mind, this is not good for production use.
Conclusion
I hope you find these code snippets and tips useful.
Give it some claps to make others find it too! Make sure you follow me on Medium not to miss anything. Also, let’s be friends on Linkedin.