Использование KeyCloak (OpenID Connect) с Apache SuperSet - PullRequest
0 голосов
/ 02 января 2019

Я начал с Используя OpenID / Keycloak с Superset и сделал все как объяснено. Однако это старый пост, и не все сработало. Я также пытаюсь реализовать собственный менеджер безопасности, установив его в качестве надстройки FAB, чтобы реализовать его в моем приложении без необходимости редактирования существующего кода надмножества.

Я использую KeyCloak 4.8.1.Final и Apache SuperSet v 0.28.1

Как объяснено в посте, SuperSet не очень хорошо работает с KeyCloak из коробки, потому что он использует OpenID 2.0, а не OpenID Connect, что и обеспечивает KeyCloak.

Первое отличие состоит в том, что после объединения запроса 4565 вы больше не можете выполнять:

from flask_appbuilder.security.sqla.manager import SecurityManager

Вместо этого теперь вы должны использовать: (согласно файлу UPDATING.md)

from superset.security import SupersetSecurityManager

В вышеупомянутом посте постер показывает, как создать менеджер и просматривать файлы отдельно, но не говорит, где его разместить. Я поместил классы менеджера и представления в один и тот же файл с именем manager.py и поместил его в структуру надстройки FAB.

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 logging

class OIDCSecurityManager(SupersetSecurityManager):
    def __init__(self,appbuilder):
        super(OIDCSecurityManager, self).__init__(appbuilder)
        if self.auth_type == AUTH_OID:
            self.oid = OpenIDConnect(self.appbuilder.get_app)
        self.authoidview = AuthOIDCView

CUSTOM_SECURITY_MANAGER = OIDCSecurityManager

class 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))

У меня установлена ​​переменная CUSTOM_SECURITY_MANAGER в этом файле, а не в superset_config.py. Это потому, что он не работал, когда он был там, он не загружал пользовательский менеджер безопасности. Я переместил туда переменную после прочтения Декоратор для SecurityManager в приложении колбы для superest .

Мой client_secret.json файл выглядит следующим образом:

{
    "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>",
        "redirect_urls": [
            "http://localhost:8001/*"
        ],
        "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"
    }
}
  • realm_public_key: я получил этот ключ в Настройки области> Ключи> Активно, а затем в таблице в строке «RS256».
  • client_id: локальный (клиент, который я использую для локального тестирования)
  • client_secret: Я получил это в разделе Клиенты> локально (из таблицы)> Учетные данные> Секрет

Все значения url / uri корректируются из первого упомянутого поста, который я использовал, чтобы все это настроить. <DOMAIN> является доменом AWS CloudFront по умолчанию, так как я использую KeyCloak на EC2 и не хочу испытывать трудности с настройкой настраиваемого домена HTTPS для простого его запуска и работы.

Тогда, наконец, часть моего superset_config.py файла выглядит так:

ADDON_MANAGERS = ['fab_addon_keycloak.manager.OIDCSecurityManager']
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = '/usr/local/lib/python3.6/site-packages/fab_addon_keycloak/fab_addon_keycloak/client_secret.json'
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
OPENID_PROVIDERS = [{
    'name': 'KeyCloak',
    'url': 'https://<DOMAIN>/auth/realms/demo/account'
}]

В исходном посте переменная окружения OPENID_PROVIDERS не упоминается, поэтому я не совсем уверен, что здесь указать для URL. Я поместил его, так как это URL, который вы нажмете, чтобы войти в клиентскую консоль на KeyCloak.

Когда я запускаю SuperSet, я не получаю никаких ошибок. Я вижу, что пользовательский менеджер безопасности загружается. Когда я перехожу к экрану входа в систему, мне нужно выбрать своего провайдера, я не получаю форму входа. Я выбираю KeyCloak, поскольку больше ничего не очевидно, и нажимаю кнопку «Войти». Когда я нажимаю «Войти», я вижу, что что-то загружается в адресной строке браузера, но ничего не происходит. Насколько я понимаю, я должен быть перенаправлен в форму входа KeyCloak, а затем вернуться к моему приложению после успешного входа, но ничего не происходит. Я что-то упустил где-то?

Редактировать

Таким образом, после некоторой дополнительной копки кажется, что мой класс пользовательского представления загружается, однако методы в классе не переопределяют поведение по умолчанию. Не уверен, почему это происходит или как это исправить.

1 Ответ

0 голосов
/ 03 января 2019

Я сам все понял.

Решение, с которым я столкнулся, не использует надстройку FAB, но вам также не нужно редактировать существующий код / ​​файлы.

Я переименовал файл manager.py в security.py, и теперь он выглядит следующим образом:

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 logging

class 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)

Я помещаю файл security.py рядом с моим файлом superset_config_py.

Файл конфигурации JSON остается без изменений.

Затем я изменил файл superset_config.py, добавив в него следующие строки:

from security import OIDCSecurityManager
AUTH_TYPE = AUTH_OID
OIDC_CLIENT_SECRETS = <path_to_configuration_file>
OIDC_ID_TOKEN_COOKIE_SECURE = False
OIDC_REQUIRE_VERIFIED_EMAIL = False
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = 'Gamma'
CUSTOM_SECURITY_MANAGER = OIDCSecurityManager

Вот и все.

Теперь, когда я перехожу на свой сайт, он автоматически переходит на экран входа в KeyCloak, и после успешного входа я перенаправлен обратно в свое приложение.

...