diff --git a/.gitignore b/.gitignore index 6da8733..28d1150 100644 --- a/.gitignore +++ b/.gitignore @@ -79,6 +79,7 @@ settings.xml .idea/**/usage.statistics.xml .idea/**/dictionaries .idea/**/shelf +.idea # AWS User-specific .idea/**/aws.xml @@ -254,4 +255,4 @@ docker/data/parquet/ !docker/data/parquet/.gitkeep data/ debezium-connect/ -scripts/distro \ No newline at end of file +scripts/distro diff --git a/docker/.env b/docker/.env index b460d64..e7c7db0 100644 --- a/docker/.env +++ b/docker/.env @@ -43,11 +43,9 @@ DATABASE_PORT=5432 ENABLE_PROXY_FIX=True REDIS_HOST=redis REDIS_PORT=6379 -SUPERSET_CLIENT_SECRET= -SUPERSET_CLIENT_ID=superset SUPERSET_HOSTNAME= ANALYTICS_DATASOURCE_NAME=PostgreSQL -ENABLE_OAUTH=false + ANALYTICS_DB_USER=analytics ANALYTICS_DB_PASSWORD=password @@ -71,7 +69,6 @@ POSTGRES_DB_HOST=postgresql SUPERSET_DB=superset SUPERSET_DB_USER=superset SUPERSET_DB_PASSWORD=password -ENABLE_OAUTH=false # Flink @@ -118,6 +115,7 @@ SUPERSET_HOME= ZOOKEEPER_URL=zookeeper:2181 -#Keycloak -KEYCLOAK_HOSTNAME= -ISSUER_URL= \ No newline at end of file +# SSO Support +ENABLE_OAUTH=false +SUPERSET_CLIENT_UUID=891b980a-9edb-4c72-a63d-1f8e488d6ad4 +SUPERSET_CLIENT_SECRET=znZK8dvk7hLOpwfU diff --git a/docker/docker-compose-db.yaml b/docker/docker-compose-db.yaml index 9f3d0c2..c54b682 100644 --- a/docker/docker-compose-db.yaml +++ b/docker/docker-compose-db.yaml @@ -1,4 +1,3 @@ -version: '3.8' services: mysql: networks: @@ -49,4 +48,4 @@ volumes: mysql-data: postgresql-data: ~ networks: - ozone-analytics: \ No newline at end of file + ozone-analytics: diff --git a/docker/docker-compose-superset.yaml b/docker/docker-compose-superset.yaml index 35360c4..8758e81 100644 --- a/docker/docker-compose-superset.yaml +++ b/docker/docker-compose-superset.yaml @@ -41,9 +41,9 @@ services: - ${SUPERSET_CONFIG_PATH}/superset_config.py:/app/superset_config.py - ${SUPERSET_CONFIG_PATH}/security.py:/app/security.py - ${SUPERSET_CONFIG_PATH}/superset-init.sh:/app/superset-init.sh - + superset-worker: - command: "celery --app=superset.tasks.celery_app:app worker --pool=gevent -Ofair -n worker1@%h --loglevel=INFO" + command: "celery --app=superset.tasks.celery_app:app worker -Ofair -n worker1@%h --loglevel=INFO" depends_on: redis: condition: service_started @@ -93,4 +93,4 @@ networks: ozone-analytics: web: external: true - name: web \ No newline at end of file + name: web diff --git a/docker/superset/config/security.py b/docker/superset/config/security.py new file mode 100644 index 0000000..2c4701e --- /dev/null +++ b/docker/superset/config/security.py @@ -0,0 +1,67 @@ +from superset.security import SupersetSecurityManager +import logging +from flask_appbuilder.security.views import AuthOAuthView +from flask_appbuilder.baseviews import expose +import os +from six.moves.urllib.parse import urlencode +import redis +import time +from flask import ( + redirect, + request, + g +) + +TOKEN_PREFIX = "oauth_id_token_" +REDIS_HOST = os.getenv("REDIS_HOST", "redis") +REDIS_PORT = os.getenv("REDIS_PORT", 6379) +redis_db = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, decode_responses=True) + +class CustomAuthOAuthView(AuthOAuthView): + + @expose("/logout/") + def logout(self, provider="keycloak", register=None): + user_id = str(g.user.id) + provider_obj = self.appbuilder.sm.oauth_remotes[provider] + redirect_url = request.url_root.strip('/') + self.appbuilder.get_url_for_login + logout_base_url = provider_obj.api_base_url + "logout" + params = { + "client_id": provider_obj.client_id, + "post_logout_redirect_uri": redirect_url + } + if redis_db.get(TOKEN_PREFIX + user_id): + params["id_token_hint"] = redis_db.get(TOKEN_PREFIX + user_id) + + ret = super().logout() + time.sleep(1) + + return redirect("{}?{}".format(logout_base_url, urlencode(params))) + + +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': + me = self.appbuilder.sm.oauth_remotes[provider].get('userinfo').json() + return { + "username": me.get("preferred_username", ""), + "first_name": me.get("given_name", ""), + "last_name": me.get("family_name", ""), + "email": me.get("email", ""), + 'roles': me.get('roles', ['Public']), + 'id_token': response["id_token"] + } + return {} + + def auth_user_oauth(self, userinfo): + user = super(CustomSecurityManager, self).auth_user_oauth(userinfo) + redis_db.set(TOKEN_PREFIX + str(user.id), userinfo["id_token"]) + del userinfo["id_token"] + roles = [self.find_role(x) for x in userinfo['roles']] + roles = [x for x in roles if x is not None] + user.roles = roles + self.update_user(user) + return user diff --git a/docker/superset/config/superset-init.sh b/docker/superset/config/superset-init.sh new file mode 100755 index 0000000..41fb7a1 --- /dev/null +++ b/docker/superset/config/superset-init.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +STEP_CNT=6 + +echo_step() { +cat <