Flask 在Apache超集上使用KeyClope(OpenID Connect)

Flask 在Apache超集上使用KeyClope(OpenID Connect),flask,openid,keycloak,apache-superset,flask-appbuilder,Flask,Openid,Keycloak,Apache Superset,Flask Appbuilder,我从一开始就按照解释做每件事。然而,这是一个老职位,并不是所有的工作。我还试图通过将其作为FAB附加组件安装来实现自定义安全管理器,以便在我的应用程序中实现它,而不必编辑现有的超集代码 我正在运行Key斗篷4.8.1.Final和Apache超集V0.28.1 正如在文章中所解释的,超集不能很好地使用开箱即用的keydove,因为它使用的是openid2.0,而不是keydove提供的openidconnect 第一个区别是,合并拉取请求后,您不能再执行以下操作: from flask_appb

我从一开始就按照解释做每件事。然而,这是一个老职位,并不是所有的工作。我还试图通过将其作为FAB附加组件安装来实现自定义安全管理器,以便在我的应用程序中实现它,而不必编辑现有的超集代码

我正在运行Key斗篷4.8.1.Final和Apache超集V0.28.1

正如在文章中所解释的,超集不能很好地使用开箱即用的keydove,因为它使用的是openid2.0,而不是keydove提供的openidconnect

第一个区别是,合并拉取请求后,您不能再执行以下操作:

from flask_appbuilder.security.sqla.manager import SecurityManager
相反,您现在必须使用:(根据updateing.md文件)

在上面提到的帖子中,海报展示了如何分别创建管理器和查看文件,但没有说明放在哪里。我将manager和view类放在同一个文件中,名为
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\u SECURITY\u MANAGER
变量,而不是在
superset\u config.py
中。这是因为它在那里时不工作,没有加载自定义安全管理器。读取后,我将变量移到了那里

我的
client\u 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"
    }
}
在最初的帖子中,没有提到
OPENID\u PROVIDERS
环境变量,因此我不确定在这里为URL放什么。我之所以这么说,是因为这是您在KeyClope上登录到客户机控制台时要点击的URL

当我运行超集时,我没有得到任何错误。我可以看到自定义安全管理器已加载。当我导航到登录屏幕时,我必须选择我的提供商,我没有得到登录表单。我选择KeyClope,因为显然没有其他东西,然后单击Login。当我单击Login时,我可以看到浏览器的地址栏中加载了一些内容,但什么也没有发生。据我所知,我应该被重定向到keydape登录表单,然后在成功登录后返回到我的应用程序,但什么也没发生。我是不是错过了什么

编辑


因此,在进一步挖掘之后,似乎我的自定义视图类加载了,但是类中的方法没有覆盖默认行为。我不知道为什么会发生这种情况,也不知道如何解决。

我最终自己解决了这个问题

我最终得到的解决方案没有使用FAB附加组件,但您也不必编辑现有代码/文件

我已将manager.py文件重命名为security.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'
}]
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文件放在我的超集配置文件旁边

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
从安全导入OIDCSecurityManager
身份验证类型=身份验证OID
OIDC_客户_机密=
OIDC\u ID\u令牌\u COOKIE\u安全=False
OIDC\u需要\u验证\u电子邮件=错误
验证用户注册=真
验证用户注册角色='Gamma'
自定义安全管理器=OIDCSecurityManager
就这样


现在,当我导航到我的站点时,它会自动转到KeyClope登录屏幕,成功登录后,我会被重定向回我的应用程序。

在superset文件夹中已经存在一个名为security.py的文件。你编辑过同一个文件吗?嗨@DeepaMG,不,我没有编辑那个文件。我的主目录“/superset/”中有一个文件夹,其中包含我的superset\u config.py文件。我将security.py和JSON文件放在superset_config.py文件旁边。如果您的superset_config.py文件位于已经有security.py文件的superset包目录中,您可以将其命名为其他名称(例如oidc_security.py),然后只需相应地更改superset_config.py文件中的引用。是的,我已经让它工作了@我现在得到的是1。在点击超集的URL时,它会将我重定向到KeyClope。2.输入凭据后,我可以看到超集。问题是我需要在KeyClope和superset中拥有相同的用户凭据。我不能只拿钥匙斗篷的证书吗?我不确定,我不这么认为。我已经设置了我的,这样,如果用户登录到KeyClope但在超集上不存在,就会自动创建一个用户。当在keydape上创建一个新用户时,它会检查超集,如果有一个用户具有相同的电子邮件地址(我认为是用户名),它会将您登录到该现有用户。在我看来,在超集中创建用户是一件好事。它允许您利用超集的内置角色来管理权限。但我正在将超集添加到我的另一个应用程序中,该应用程序已与缺少用户的KeyClope集成。我不想在超集中再次创建相同的凭据。
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