Skip to content

Commit

Permalink
Feat: change login to oauth via keycloak (#47)
Browse files Browse the repository at this point in the history
### SUMMARY
Change login via keycloak

### BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

![image](https://github.com/user-attachments/assets/1ec1624e-39f4-4005-be35-4b0e5dd2933e)

### ADDITIONAL INFORMATION
- [ ] Has associated issue:
- [ ] Required feature flags:
- [ ] Changes UI
- [ ] Includes DB Migration (follow approval process in
[SIP-59](apache#13351))
  - [ ] Migration is atomic, supports rollback & is backwards-compatible
  - [ ] Confirm DB migration upgrade and downgrade tested
  - [ ] Runtime estimates and downtime expectations provided
- [X] Introduces new feature or API
- [ ] Removes existing feature or API
  • Loading branch information
HariSeldon23 authored Nov 27, 2024
2 parents 454d4d0 + 595eb46 commit 9e2d317
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 3 deletions.
2 changes: 1 addition & 1 deletion requirements/local.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ prophet==1.1.3
pydoris==1.0.5.3
sqlalchemy-bigquery==1.11.0
shillelagh[gsheetsapi]
Authlib
Authlib==1.3.2
47 changes: 45 additions & 2 deletions superset/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
import pkg_resources
from celery.schedules import crontab
from flask import Blueprint
from flask_appbuilder.security.manager import AUTH_DB
from flask_appbuilder.security.manager import AUTH_OAUTH
from flask_caching.backends.base import BaseCache
from pandas import Series
from pandas._libs.parsers import STR_NA_VALUES # pylint: disable=no-name-in-module
Expand Down Expand Up @@ -310,7 +310,50 @@ def _try_json_readsha(filepath: str, length: int) -> str | None:
# AUTH_DB : Is for database (username/password)
# AUTH_LDAP : Is for LDAP
# AUTH_REMOTE_USER : Is for using REMOTE_USER from web server
AUTH_TYPE = AUTH_DB
AUTH_TYPE = AUTH_OAUTH

KEYCLOAK_CLIENT_SECRET = os.environ.get("KEYCLOAK_CLIENT_SECRET", "")
KEYCLOAK_CLIENT_ID = os.environ.get("KEYCLOAK_CLIENT_ID", "")
KEYCLOAK_URL = os.environ.get("KEYCLOAK_URL", "")
KEYCLOAK_REALM = os.environ.get("KEYCLOAK_REALM", "")

# Setup auth via OAUTH in keycloak
OAUTH_PROVIDERS = [{
'name': 'keycloak',
'icon': 'fa-key',
'token_key': 'access_token',
'remote_app': {
'client_id': KEYCLOAK_CLIENT_ID ,
'client_secret': KEYCLOAK_CLIENT_SECRET,
'api_base_url': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/',
'jwks_uri': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/certs',
'server_metadata_url': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/.well-known/openid-configuration',
'client_kwargs': {
'scope': 'openid email profile roles',
'redirect_uri': 'http://localhost:8088/oauth-authorized/keycloak',
'userinfo_uri': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/userinfo',
'verify_signature': True,
'verify_exp': True
},
'request_token_url': None,
'access_token_url': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/token',
'authorize_url': f'{KEYCLOAK_URL}/realms/{KEYCLOAK_REALM}/protocol/openid-connect/auth'
},
}]

# URL Configuration
PREFERRED_URL_SCHEME = 'https'

# Authentication Configuration
AUTH_USER_REGISTRATION = True
AUTH_USER_REGISTRATION_ROLE = "sql_lab"
AUTH_ROLES_SYNC_AT_LOGIN = True
AUTH_ROLES_MAPPING = {
"studio_admin": ["Admin"],
"studio_alpha": ["Alpha"],
"studio_gamma": ["Gamma"],
"studio_sqllab": ["sql_lab"],
}

# Uncomment to setup Full admin role name
# AUTH_ROLE_ADMIN = 'Admin'
Expand Down
18 changes: 18 additions & 0 deletions superset/security/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,24 @@ def send_pw_reset_email(self, user, resetpw):
return False
return True

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('openid-connect/userinfo')
me.raise_for_status()
data = me.json()
logging.debug(f"user_data: {data}")
return {
"name" : data.get("name", ""),
"email" : data.get("email", ""),
"id": data.get("sub", ""),
"username": data.get("preferred_username", ""),
"first_name": data.get("given_name", ""),
"last_name": data.get("family_name", ""),
"email": data.get("email", ""),
"role_keys": data.get("role_keys", []),
}

def get_schema_perm(
self, database: Union["Database", str], schema: Optional[str] = None
) -> Optional[str]:
Expand Down

0 comments on commit 9e2d317

Please sign in to comment.