在Apache SuperSet中使用KeyCloak(OpenID Connect) [英] Using KeyCloak(OpenID Connect) with Apache SuperSet
问题描述
我首先使用将OpenID/Keycloak与Superset结合使用,并按说明进行了一切.但是,这是一个过时的帖子,并非所有工作都奏效.我还试图通过将其安装为FAB附加组件来实现自定义安全管理器,以便在我的应用程序中实现它而不必编辑现有的超集代码.
I started with Using OpenID/Keycloak with Superset and did everything as explained. However, it is an old post, and not everything worked. I'm also trying to implement a custom security manager by installing it as a FAB add-on, so as to implement it in my application without having to edit the existing superset code.
我正在运行KeyCloak 4.8.1.Final和Apache SuperSet v 0.28.1
I'm running KeyCloak 4.8.1.Final and Apache SuperSet v 0.28.1
如文章中所述,SuperSet不能很好地与KeyCloak配合使用,因为它使用的是OpenID 2.0,而不是KeyCloak提供的OpenID Connect.
As explained in the post, SuperSet does not play nicely with KeyCloak out of the box because it uses OpenID 2.0 and not OpenID Connect, which is what KeyCloak provides.
第一个区别是,在请求请求 4565 被合并之后,您不能再做:
The first difference is that after pull request 4565 was merged, you can no longer do:
from flask_appbuilder.security.sqla.manager import SecurityManager
相反,您现在必须使用:(根据UPDATING.md文件)
Instead, you now have to use: (as per the UPDATING.md file)
from superset.security import SupersetSecurityManager
在上述文章中,张贴者展示了如何分别创建管理器和查看文件,但没有说明将文件放置在何处.我将管理类和视图类都放在了一个名为manager.py
的文件中,并将其放在FAB附加结构中.
In the above mentioned post, the poster shows how to create the manager and view files separately, but don't say where to put it. I placed both the manager and view classes in the same file, named manager.py
, and placed it in the FAB add-on structure.
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的装饰器以获取最高表现后,我将变量移到了那里/a>.
I have the CUSTOM_SECURITY_MANAGER
variable set in this file and not in superset_config.py
. This is because it didn't work when it was there, it didn't load the custom security manager. I moved the variable there after reading Decorator for SecurityManager in flask appbuilder for 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
:我是在客户端">本地"(从表中)>凭证">秘密" 处获得此信息的
realm_public_key
: I got this key at Realm Settings > Keys > Active and then in the table, in the "RS256" row.client_id
: local (the client I use for local testing)client_secret
: I got this at Clients > local (from the table) > Credentials > Secret
所有的url/uri值都从我之前设置的所有提到的第一篇文章中进行了调整. <DOMAIN>
是AWS CloudFront的默认域,因为我在EC2上运行KeyCloak,并且不想麻烦地设置自定义HTTPS域以简单地启动并运行它.
All the url/uri values are adjusted from the first mentioned post I used to set it all up. The <DOMAIN>
is an AWS CloudFront default domain, since I'm running KeyCloak on EC2 and don't want to go through the trouble to setup a custom HTTPS domain for simply getting it up and running.
然后,最后,我的superset_config.py
文件的一部分看起来像这样:
Then, finally, part of my superset_config.py
file looks like this:
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中输入什么内容.我把它放在那是因为您在登录KeyCloak上的客户端控制台时将使用该URL.
In the original post, the OPENID_PROVIDERS
environment variable is not mentioned, so I'm not really sure what to put in here for the URL. I put that one since that's the URL you'll hit to login to the client console on KeyCloak.
当我运行SuperSet时,没有任何错误.我可以看到自定义安全管理器已加载.当我导航到登录屏幕时,我必须选择我的提供者,但是我没有登录表单.我选择KeyCloak,因为显然没有其他选择,然后单击Login.当我单击登录"时,可以看到浏览器的地址栏中加载了某些内容,但没有任何反应.据我了解,应该将我重定向到KeyCloak登录表单,然后在成功登录后再返回到我的应用程序,但是什么也没有发生.我在某处想念东西吗?
When I run SuperSet I don't get any errors. I can see that the custom security manager loads. When I navigate to the login screen, I have to choose my provider, I don't get a login form. I choose KeyCloak, since there's obviously nothing else, and click Login. When I click Login I can see that something loads in the address bar of the browser, but nothing happens. It's my understanding that I'm supposed to be redirected to the KeyCloak login form, and then back to my application upon successful login, but nothing happens. Am I missing something somewhere?
修改
因此,在进行更多挖掘之后,似乎加载了我的自定义视图类,但是该类中的方法不会覆盖默认行为.不知道为什么会这样或如何解决.
So after some more digging, it seems like my custom view class loads, however the methods in the class do not override the default behavior. Not sure why this is happening or how to fix it.
推荐答案
我最终自己弄清楚了.
我最终得到的解决方案没有使用FAB附加组件,但是您也不必编辑现有的代码/文件.
The solution I ended up with does not make use of a FAB add-on, but you also don't have to edit existing code/files.
我已将manager.py文件重命名为security.py,现在看起来像这样:
I've renamed the manager.py file to security.py, and it now 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 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文件旁边.
I place the security.py file next to my superset_config_py file.
JSON配置文件保持不变.
The JSON configuration file stays unchanged.
然后,我将superset_config.py文件更改为包括以下行:
Then I've changed the superset_config.py file to include the following lines:
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登录屏幕,成功登录后,我将被重定向回我的应用程序.
Now when I navigate to my site, it automatically goes to the KeyCloak login screen, and upon successful sign in I am redirected back to my application.
这篇关于在Apache SuperSet中使用KeyCloak(OpenID Connect)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!