Skip to content

Commit

Permalink
OZ-671: Superset config to use 'authlib' instead of Flask OIDC (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
enyachoke authored Sep 5, 2024
1 parent 44ef607 commit fc791ee
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 91 deletions.
102 changes: 47 additions & 55 deletions distro/configs/superset/security.py
Original file line number Diff line number Diff line change
@@ -1,58 +1,50 @@
from flask import redirect, request
from flask_appbuilder.security.manager import AUTH_OID
from math import log
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 logging
logger = logging.getLogger(__name__)

class AuthOIDCView(AuthOIDView):
def add_role_if_missing(self, sm, user_id, role_name):
found_role = sm.find_role(role_name)
session = sm.get_session
user = session.query(sm.user_model).get(user_id)
if found_role and found_role not in user.roles:
user.roles += [found_role]
session.commit()

@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','roles'])
user = sm.add_user(info.get('preferred_username'), info.get('given_name'), info.get('family_name'), info.get('email'), sm.find_role('Gamma'))
role_info = oidc.user_getinfo(['roles'])
if role_info is not None:
for role in role_info['roles']:
self.add_role_if_missing(sm, user.id, role)
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()
from flask_appbuilder.security.views import AuthOAuthView
from flask_appbuilder.baseviews import expose
import time
from flask import (
redirect,
request
)

class CustomAuthOAuthView(AuthOAuthView):

@expose("/logout/")
def logout(self, provider="keycloak", register=None):
provider_obj = self.appbuilder.sm.oauth_remotes[provider]
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)
url = ("logout?client_id={}&post_logout_redirect_uri={}".format(
provider_obj.client_id,
redirect_url
))

ret = super().logout()
time.sleep(1)

return redirect("{}{}".format(provider_obj.api_base_url, url))


class CustomSecurityManager(SupersetSecurityManager):
# override the logout function
authoauthview = CustomAuthOAuthView

def oauth_user_info(self, provider, response=None):
logging.debug("Oauth2 provider: {0}.".format(provider))
if provider == 'keycloak':
# superset_roles: list[str] = ["Admin", "Alpha", "Gamma", "Public", "granter", "sql_lab"]
me = self.appbuilder.sm.oauth_remotes[provider].get('userinfo').json()
roles = ["public", ]
if "roles" in me:
role_prefix = "superset-"
roles = [r[len(role_prefix):].lower() for r in me.get("roles", []) if r.startswith(role_prefix)]

return {
"username": me.get("preferred_username", ""),
"first_name": me.get("given_name", ""),
"last_name": me.get("family_name", ""),
"email": me.get("email", ""),
"role_keys": roles,
}
return {}
62 changes: 26 additions & 36 deletions distro/configs/superset/superset_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from cachelib import RedisCache

from cachelib.file import FileSystemCache

logger = logging.getLogger()

def password_from_env(url):
Expand Down Expand Up @@ -70,34 +69,12 @@ class CeleryConfig(object):
'CACHE_REDIS_HOST': 'redis',
'CACHE_REDIS_PORT': 6379,
'CACHE_REDIS_DB': 1,
'CACHE_REDIS_URL': f"redis://{REDIS_HOST}:{REDIS_PORT}/1"
}

FILTER_STATE_CACHE_CONFIG = {
'CACHE_TYPE': 'RedisCache',
'CACHE_DEFAULT_TIMEOUT': 300,
'CACHE_KEY_PREFIX': 'superset_filter_',
'CACHE_REDIS_PORT': 6379,
'CACHE_REDIS_DB': 2,
'CACHE_REDIS_URL': f"redis://{REDIS_HOST}:{REDIS_PORT}/2"
}

EXPLORE_FORM_DATA_CACHE_CONFIG = {
'CACHE_TYPE': 'RedisCache',
'CACHE_DEFAULT_TIMEOUT': 300,
'CACHE_KEY_PREFIX': 'superset_form_date_',
'CACHE_REDIS_PORT': 6379,
'CACHE_REDIS_DB': 3,
'CACHE_REDIS_URL': f"redis://{REDIS_HOST}:{REDIS_PORT}/3"
'CACHE_REDIS_URL': 'redis://redis:6379/1'
}

CELERY_CONFIG = CeleryConfig
SQLLAB_CTAS_NO_LIMIT = True
PERMANENT_SESSION_LIFETIME = 86400
FEATURE_FLAGS = {
"DASHBOARD_RBAC": True,
'ENABLE_TEMPLATE_PROCESSING': True,
}

class ReverseProxied(object):

Expand All @@ -120,20 +97,33 @@ def __call__(self, environ, start_response):

ADDITIONAL_MIDDLEWARE = [ReverseProxied, ]
ENABLE_PROXY_FIX = True
PREVENT_UNSAFE_DB_CONNECTIONS = False

# Enable the security manager API.
FAB_ADD_SECURITY_API = True

if os.getenv("ENABLE_OAUTH") == "true":
from security import OIDCSecurityManager
from flask_appbuilder.security.manager import AUTH_OID
KEYCLOAK_URL = os.getenv('KEYCLOAK_URL')
SUPERSET_CLIENT_SECRET = os.getenv('SUPERSET_CLIENT_SECRET')
SUPERSET_URL = os.getenv('SUPERSET_URL')
AUTH_TYPE = AUTH_OID
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
from flask_appbuilder.security.manager import AUTH_OAUTH
from security import CustomSecurityManager
AUTH_ROLES_SYNC_AT_LOGIN = True
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager
OIDC_CLIENT_SECRETS = '/etc/superset/client_secret.json'
AUTH_USER_REGISTRATION_ROLE = "Gamma"
CUSTOM_SECURITY_MANAGER = CustomSecurityManager
LOGOUT_REDIRECT_URL = os.environ.get("SUPERSET_URL")
AUTH_TYPE = AUTH_OAUTH
OAUTH_PROVIDERS = [
{
'name': 'keycloak',
'token_key': 'access_token', # Name of the token in the response of access_token_url
'icon': 'fa-key', # Icon for the provider
'remote_app': {
'client_id': os.environ.get("SUPERSET_CLIENT_ID","superset"), # Client Id (Identify Superset application)
'client_secret': os.environ.get("SUPERSET_CLIENT_SECRET"), # Secret for this Client Id (Identify Superset application)
'api_base_url': os.environ.get("ISSUER_URL").rstrip('/') + "/protocol/openid-connect/",
'client_kwargs': {
'scope': 'openid profile email',
},
'logout_redirect_uri': os.environ.get("SUPERSET_URL"),
'server_metadata_url': os.environ.get("ISSUER_URL").rstrip('/') + '/.well-known/openid-configuration', # URL to get metadata from
}
}
]

0 comments on commit fc791ee

Please sign in to comment.