codeant-ai-for-open-source[bot] commented on code in PR #38092:
URL: https://github.com/apache/superset/pull/38092#discussion_r2826935644


##########
docs/docs/installation/kubernetes.mdx:
##########
@@ -308,6 +308,122 @@ configOverrides:
     AUTH_USER_REGISTRATION_ROLE = "Admin"
 ```
 
+:::note
+
+Here another example with groups mapping, logout from Keycloak and PKCE flow.
+You need to create a Secret that contains CLIENT_ID, CLIENT_SECRET and 
OIDC_ISSUER.
+
+Your oauth provider must be configured to support PKCE flow.
+For keycloak for example, you need to set 'Proof Key for Code Exchange Code 
Challenge Method' to value S256 under:
+Clients -> <your-client-id> -> Advanced -> Advanced settings
+
+:::
+
+```yaml
+extraSecretEnv:
+  CLIENT_ID: my_superset_clientid
+  CLIENT_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+  OIDC_ISSUER: https://myoauthprovider.youpi.fr/auth/realms/MYREALM
+
+configOverrides:
+  enable_oauth: |
+    from flask import redirect, request, session
+    from flask_appbuilder import expose
+    from flask_appbuilder.security.manager import AUTH_OAUTH
+    from flask_appbuilder.security.views import AuthOAuthView
+    from superset.security import SupersetSecurityManager
+    import logging
+    import os
+    import requests
+
+    log = logging.getLogger(__name__)
+
+    AUTH_TYPE = AUTH_OAUTH
+    AUTH_USER_REGISTRATION = True
+    AUTH_ROLES_SYNC_AT_LOGIN = True
+    AUTH_USER_REGISTRATION_ROLE = 'Public'
+
+    PROVIDER_NAME = 'keycloak'
+    CLIENT_ID = os.getenv('CLIENT_ID')
+    CLIENT_SECRET = os.getenv('CLIENT_SECRET')
+    OIDC_ISSUER = os.getenv('OIDC_ISSUER')
+    OIDC_BASE_URL = f'{OIDC_ISSUER}/protocol/openid-connect'
+    OIDC_AUTH_URL = f'{OIDC_BASE_URL}/auth'
+    OIDC_METADATA_URL = f'{OIDC_ISSUER}/.well-known/openid-configuration'
+    OIDC_TOKEN_URL = f'{OIDC_BASE_URL}/token'
+    OIDC_USERINFO_URL = f'{OIDC_BASE_URL}/userinfo'
+    OAUTH_PROVIDERS = [
+        {
+            'name': PROVIDER_NAME,
+            'token_key': 'access_token',
+            'icon': 'fa-circle-o',
+            'remote_app': {
+                'api_base_url': OIDC_BASE_URL,
+                'access_token_url': OIDC_TOKEN_URL,
+                'authorize_url': OIDC_AUTH_URL,
+                'request_token_url': None,
+                'server_metadata_url': OIDC_METADATA_URL,
+                'client_id': CLIENT_ID,
+                'client_secret': CLIENT_SECRET,
+                'client_kwargs': {
+                    'scope': 'openid email profile',
+                    'code_challenge_method': 'S256',
+                    'response_type': 'code',
+                },
+            },
+        }
+    ]
+
+    # Make sure you create these groups on Keycloak
+    AUTH_ROLES_MAPPING = {
+        f'{CLIENT_ID}_admin': ['Admin'],
+        f'{CLIENT_ID}_alpha': ['Alpha'],
+        f'{CLIENT_ID}_gamma': ['Gamma'],
+        f'{CLIENT_ID}_public': ['Public'],
+    }
+
+
+    # Custom OAuth view to handle logout redirection to Keycloak
+    class CustomOAuthView(AuthOAuthView):
+        @expose('/logout/', methods=['GET', 'POST'])
+        def logout(self):
+            session.clear()
+            return redirect(
+                
f'{OIDC_ISSUER}/protocol/openid-connect/logout?post_logout_redirect_uri={request.url_root.strip("/")}&client_id={CLIENT_ID}'
+            )
+
+
+    class CustomSsoSecurityManager(SupersetSecurityManager):
+        # Override the default OAuth view with our custom one
+        authoauthview = CustomOAuthView
+
+        # Override the method to fetch groups from the token and map them to 
Superset roles
+        def get_oauth_user_info(self, provider, response):
+            if provider == 'keycloak':
+                headers = {'Authorization': f"Bearer 
{response['access_token']}"}
+                resp = requests.get(OIDC_USERINFO_URL, headers=headers)
+                me = resp.json()
+
+                log.info(f'userinfo: {me}')
+
+                groups = me.get('groups', [])
+                if not groups:
+                    groups = ['Public']
+

Review Comment:
   **Suggestion:** The default group assigned when no groups are present 
(`"Public"`) does not match any key in the roles mapping (which uses names like 
`<CLIENT_ID>_public`), so users without a `groups` claim will not get any 
mapped roles, likely preventing them from receiving the intended default 
"Public" role. [logic error]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ⚠️ Keycloak-based OAuth example misconfigures default group mapping.
   - ⚠️ Users without groups may miss intended Public permissions.
   - ⚠️ Role sync from IdP groups behaves inconsistently with docs.
   ```
   </details>
   
   ```suggestion
                       groups = [f'{CLIENT_ID}_public']
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Deploy Superset on Kubernetes using the Helm chart and copy the Keycloak 
example from
   `docs/docs/installation/kubernetes.mdx` under `### Setting up OAuth`, 
specifically the
   `configOverrides.enable_oauth` block that defines
   `CustomSsoSecurityManager.get_oauth_user_info` (around lines 396–424 in the 
final file).
   
   2. In Keycloak, configure the client with ID `my_superset_clientid` 
(matching `CLIENT_ID`
   in the example) and create realm roles / groups such that some user has no 
groups (or the
   `groups` claim is omitted/empty in the ID/access token / userinfo response).
   
   3. Log in to Superset via the Keycloak OAuth provider (the `keycloak` 
provider defined in
   `OAUTH_PROVIDERS` in the same config block). Superset invokes
   `CustomSsoSecurityManager.get_oauth_user_info('keycloak', response)` which 
calls the
   snippet at `docs/docs/installation/kubernetes.mdx:410–412` to derive 
`groups`.
   
   4. Because `groups` is empty, the code sets `groups = ['Public']`, but
   `AUTH_ROLES_MAPPING` (defined earlier in the same config block at lines 
~378–383) only
   contains keys like `f'{CLIENT_ID}_public'`. As a result, Superset's role 
sync logic (in
   `superset/security/manager.py`, which reads `AUTH_ROLES_MAPPING`) cannot map 
`['Public']`
   to any Superset role, so the user does not receive the intended `Public` 
role derived from
   group mapping. This misalignment demonstrates the logic bug the suggestion 
addresses.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** docs/docs/installation/kubernetes.mdx
   **Line:** 412:412
   **Comment:**
        *Logic Error: The default group assigned when no groups are present 
(`"Public"`) does not match any key in the roles mapping (which uses names like 
`<CLIENT_ID>_public`), so users without a `groups` claim will not get any 
mapped roles, likely preventing them from receiving the intended default 
"Public" role.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=00040b1c110b801ce2a53b1fc25d726b52e114cf8a95569d315f725d27ec3527&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=00040b1c110b801ce2a53b1fc25d726b52e114cf8a95569d315f725d27ec3527&reaction=dislike'>👎</a>



##########
docs/docs/installation/kubernetes.mdx:
##########
@@ -308,6 +308,122 @@ configOverrides:
     AUTH_USER_REGISTRATION_ROLE = "Admin"
 ```
 
+:::note
+
+Here another example with groups mapping, logout from Keycloak and PKCE flow.
+You need to create a Secret that contains CLIENT_ID, CLIENT_SECRET and 
OIDC_ISSUER.
+
+Your oauth provider must be configured to support PKCE flow.
+For keycloak for example, you need to set 'Proof Key for Code Exchange Code 
Challenge Method' to value S256 under:
+Clients -> <your-client-id> -> Advanced -> Advanced settings
+
+:::
+
+```yaml
+extraSecretEnv:
+  CLIENT_ID: my_superset_clientid
+  CLIENT_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+  OIDC_ISSUER: https://myoauthprovider.youpi.fr/auth/realms/MYREALM
+
+configOverrides:
+  enable_oauth: |
+    from flask import redirect, request, session
+    from flask_appbuilder import expose
+    from flask_appbuilder.security.manager import AUTH_OAUTH
+    from flask_appbuilder.security.views import AuthOAuthView
+    from superset.security import SupersetSecurityManager
+    import logging
+    import os
+    import requests
+
+    log = logging.getLogger(__name__)
+
+    AUTH_TYPE = AUTH_OAUTH
+    AUTH_USER_REGISTRATION = True
+    AUTH_ROLES_SYNC_AT_LOGIN = True
+    AUTH_USER_REGISTRATION_ROLE = 'Public'
+
+    PROVIDER_NAME = 'keycloak'
+    CLIENT_ID = os.getenv('CLIENT_ID')
+    CLIENT_SECRET = os.getenv('CLIENT_SECRET')
+    OIDC_ISSUER = os.getenv('OIDC_ISSUER')
+    OIDC_BASE_URL = f'{OIDC_ISSUER}/protocol/openid-connect'
+    OIDC_AUTH_URL = f'{OIDC_BASE_URL}/auth'
+    OIDC_METADATA_URL = f'{OIDC_ISSUER}/.well-known/openid-configuration'
+    OIDC_TOKEN_URL = f'{OIDC_BASE_URL}/token'
+    OIDC_USERINFO_URL = f'{OIDC_BASE_URL}/userinfo'
+    OAUTH_PROVIDERS = [
+        {
+            'name': PROVIDER_NAME,
+            'token_key': 'access_token',
+            'icon': 'fa-circle-o',
+            'remote_app': {
+                'api_base_url': OIDC_BASE_URL,
+                'access_token_url': OIDC_TOKEN_URL,
+                'authorize_url': OIDC_AUTH_URL,
+                'request_token_url': None,
+                'server_metadata_url': OIDC_METADATA_URL,
+                'client_id': CLIENT_ID,
+                'client_secret': CLIENT_SECRET,
+                'client_kwargs': {
+                    'scope': 'openid email profile',
+                    'code_challenge_method': 'S256',
+                    'response_type': 'code',
+                },
+            },
+        }
+    ]
+
+    # Make sure you create these groups on Keycloak
+    AUTH_ROLES_MAPPING = {
+        f'{CLIENT_ID}_admin': ['Admin'],
+        f'{CLIENT_ID}_alpha': ['Alpha'],
+        f'{CLIENT_ID}_gamma': ['Gamma'],
+        f'{CLIENT_ID}_public': ['Public'],
+    }
+
+
+    # Custom OAuth view to handle logout redirection to Keycloak
+    class CustomOAuthView(AuthOAuthView):
+        @expose('/logout/', methods=['GET', 'POST'])
+        def logout(self):
+            session.clear()
+            return redirect(
+                
f'{OIDC_ISSUER}/protocol/openid-connect/logout?post_logout_redirect_uri={request.url_root.strip("/")}&client_id={CLIENT_ID}'
+            )
+
+
+    class CustomSsoSecurityManager(SupersetSecurityManager):
+        # Override the default OAuth view with our custom one
+        authoauthview = CustomOAuthView
+
+        # Override the method to fetch groups from the token and map them to 
Superset roles
+        def get_oauth_user_info(self, provider, response):
+            if provider == 'keycloak':
+                headers = {'Authorization': f"Bearer 
{response['access_token']}"}
+                resp = requests.get(OIDC_USERINFO_URL, headers=headers)
+                me = resp.json()
+
+                log.info(f'userinfo: {me}')
+

Review Comment:
   **Suggestion:** Logging the entire `userinfo` payload at info level will 
store personally identifiable information (like emails and full profile data) 
in standard logs, which is a security and privacy risk in production 
environments. [security]
   
   <details>
   <summary><b>Severity Level:</b> Critical 🚨</summary>
   
   ```mdx
   - ❌ Keycloak OAuth logins leak PII into app logs.
   - ⚠️ Centralized logging stores emails and profile attributes.
   - ⚠️ Harder to meet privacy and compliance requirements.
   ```
   </details>
   
   ```suggestion
   log.debug("Received userinfo from OIDC provider for user %s", 
me.get("preferred_username"))
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Deploy Superset on Kubernetes with the Keycloak `enable_oauth` example 
from
   `docs/docs/installation/kubernetes.mdx` copied verbatim into
   `configOverrides.enable_oauth`, including the `log.info(f'userinfo: {me}')` 
line at
   approximately line 408.
   
   2. Configure logging for the Superset webserver to use the default INFO log 
level (the
   standard production setting for many deployments).
   
   3. Have any user authenticate via the configured Keycloak OAuth provider 
(`provider` name
   `keycloak` as defined in `OAUTH_PROVIDERS` in the same config block). During 
login,
   Superset calls `CustomSsoSecurityManager.get_oauth_user_info('keycloak', 
response)` which
   issues the userinfo request and assigns `me = resp.json()`.
   
   4. The line `log.info(f'userinfo: {me}')` at 
`docs/docs/installation/kubernetes.mdx:408`
   logs the full `userinfo` JSON including email, full name, username, and 
groups to the
   application logs on every login, making this PII available to anyone with 
access to logs
   and potentially to log aggregation systems.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** docs/docs/installation/kubernetes.mdx
   **Line:** 408:408
   **Comment:**
        *Security: Logging the entire `userinfo` payload at info level will 
store personally identifiable information (like emails and full profile data) 
in standard logs, which is a security and privacy risk in production 
environments.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=a05101bd0c458359bba7ea29d786591843ff106325df28fa0c4da01cbcbeca97&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=a05101bd0c458359bba7ea29d786591843ff106325df28fa0c4da01cbcbeca97&reaction=dislike'>👎</a>



##########
docs/docs/installation/kubernetes.mdx:
##########
@@ -308,6 +308,122 @@ configOverrides:
     AUTH_USER_REGISTRATION_ROLE = "Admin"
 ```
 
+:::note
+
+Here another example with groups mapping, logout from Keycloak and PKCE flow.
+You need to create a Secret that contains CLIENT_ID, CLIENT_SECRET and 
OIDC_ISSUER.
+
+Your oauth provider must be configured to support PKCE flow.
+For keycloak for example, you need to set 'Proof Key for Code Exchange Code 
Challenge Method' to value S256 under:
+Clients -> <your-client-id> -> Advanced -> Advanced settings
+
+:::
+
+```yaml
+extraSecretEnv:
+  CLIENT_ID: my_superset_clientid
+  CLIENT_SECRET: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+  OIDC_ISSUER: https://myoauthprovider.youpi.fr/auth/realms/MYREALM
+
+configOverrides:
+  enable_oauth: |
+    from flask import redirect, request, session
+    from flask_appbuilder import expose
+    from flask_appbuilder.security.manager import AUTH_OAUTH
+    from flask_appbuilder.security.views import AuthOAuthView
+    from superset.security import SupersetSecurityManager
+    import logging
+    import os
+    import requests
+
+    log = logging.getLogger(__name__)
+
+    AUTH_TYPE = AUTH_OAUTH
+    AUTH_USER_REGISTRATION = True
+    AUTH_ROLES_SYNC_AT_LOGIN = True
+    AUTH_USER_REGISTRATION_ROLE = 'Public'
+
+    PROVIDER_NAME = 'keycloak'
+    CLIENT_ID = os.getenv('CLIENT_ID')
+    CLIENT_SECRET = os.getenv('CLIENT_SECRET')
+    OIDC_ISSUER = os.getenv('OIDC_ISSUER')
+    OIDC_BASE_URL = f'{OIDC_ISSUER}/protocol/openid-connect'
+    OIDC_AUTH_URL = f'{OIDC_BASE_URL}/auth'
+    OIDC_METADATA_URL = f'{OIDC_ISSUER}/.well-known/openid-configuration'
+    OIDC_TOKEN_URL = f'{OIDC_BASE_URL}/token'
+    OIDC_USERINFO_URL = f'{OIDC_BASE_URL}/userinfo'
+    OAUTH_PROVIDERS = [
+        {
+            'name': PROVIDER_NAME,
+            'token_key': 'access_token',
+            'icon': 'fa-circle-o',
+            'remote_app': {
+                'api_base_url': OIDC_BASE_URL,
+                'access_token_url': OIDC_TOKEN_URL,
+                'authorize_url': OIDC_AUTH_URL,
+                'request_token_url': None,
+                'server_metadata_url': OIDC_METADATA_URL,
+                'client_id': CLIENT_ID,
+                'client_secret': CLIENT_SECRET,
+                'client_kwargs': {
+                    'scope': 'openid email profile',
+                    'code_challenge_method': 'S256',
+                    'response_type': 'code',
+                },
+            },
+        }
+    ]
+
+    # Make sure you create these groups on Keycloak
+    AUTH_ROLES_MAPPING = {
+        f'{CLIENT_ID}_admin': ['Admin'],
+        f'{CLIENT_ID}_alpha': ['Alpha'],
+        f'{CLIENT_ID}_gamma': ['Gamma'],
+        f'{CLIENT_ID}_public': ['Public'],
+    }
+
+
+    # Custom OAuth view to handle logout redirection to Keycloak
+    class CustomOAuthView(AuthOAuthView):
+        @expose('/logout/', methods=['GET', 'POST'])
+        def logout(self):
+            session.clear()
+            return redirect(
+                
f'{OIDC_ISSUER}/protocol/openid-connect/logout?post_logout_redirect_uri={request.url_root.strip("/")}&client_id={CLIENT_ID}'
+            )
+
+
+    class CustomSsoSecurityManager(SupersetSecurityManager):
+        # Override the default OAuth view with our custom one
+        authoauthview = CustomOAuthView
+
+        # Override the method to fetch groups from the token and map them to 
Superset roles
+        def get_oauth_user_info(self, provider, response):
+            if provider == 'keycloak':
+                headers = {'Authorization': f"Bearer 
{response['access_token']}"}
+                resp = requests.get(OIDC_USERINFO_URL, headers=headers)
+                me = resp.json()

Review Comment:
   **Suggestion:** The call to the OIDC userinfo endpoint uses `requests.get` 
without a timeout, so if the identity provider is slow or unresponsive, login 
requests can hang indefinitely and tie up worker processes. [possible bug]
   
   <details>
   <summary><b>Severity Level:</b> Major ⚠️</summary>
   
   ```mdx
   - ❌ Keycloak-based logins can hang during IdP outages.
   - ⚠️ Webserver workers may be exhausted under partial outages.
   - ⚠️ User authentication reliability reduced during network issues.
   ```
   </details>
   
   ```suggestion
                   resp = requests.get(OIDC_USERINFO_URL, headers=headers, 
timeout=5)
   ```
   <details>
   <summary><b>Steps of Reproduction ✅ </b></summary>
   
   ```mdx
   1. Deploy Superset using the Kubernetes Helm chart and copy the Keycloak 
OAuth example
   from `docs/docs/installation/kubernetes.mdx` into 
`configOverrides.enable_oauth`,
   including the `requests.get(OIDC_USERINFO_URL, headers=headers)` call in
   `CustomSsoSecurityManager.get_oauth_user_info` (around line 405).
   
   2. Configure the `CLIENT_ID`, `CLIENT_SECRET`, and `OIDC_ISSUER` environment 
variables as
   described in the same example so that `OIDC_USERINFO_URL` points at your IdP 
(e.g.,
   Keycloak `.../protocol/openid-connect/userinfo`).
   
   3. Simulate an IdP/network issue by making the OIDC userinfo endpoint slow 
or unresponsive
   (for example, by adding a firewall rule that drops packets to the userinfo 
URL, or by
   stopping the Keycloak instance while keeping DNS/resolution unchanged).
   
   4. Have a user attempt to log in via the Keycloak OAuth provider. The 
Superset webserver
   calls `requests.get(OIDC_USERINFO_URL, headers=headers)` at
   `docs/docs/installation/kubernetes.mdx:405` with no timeout, causing the 
request (and
   therefore the login HTTP request) to hang until the underlying HTTP stack or 
webserver
   kills the worker, tying up resources and degrading availability.
   ```
   </details>
   <details>
   <summary><b>Prompt for AI Agent 🤖 </b></summary>
   
   ```mdx
   This is a comment left during a code review.
   
   **Path:** docs/docs/installation/kubernetes.mdx
   **Line:** 405:405
   **Comment:**
        *Possible Bug: The call to the OIDC userinfo endpoint uses 
`requests.get` without a timeout, so if the identity provider is slow or 
unresponsive, login requests can hang indefinitely and tie up worker processes.
   
   Validate the correctness of the flagged issue. If correct, How can I resolve 
this? If you propose a fix, implement it and please make it concise.
   ```
   </details>
   <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=f26999d3e2048e7b35e6b8b097e2abc127a752993c889c4086d8ecad89d15bf0&reaction=like'>👍</a>
 | <a 
href='https://app.codeant.ai/feedback?pr_url=https%3A%2F%2Fgithub.com%2Fapache%2Fsuperset%2Fpull%2F38092&comment_hash=f26999d3e2048e7b35e6b8b097e2abc127a752993c889c4086d8ecad89d15bf0&reaction=dislike'>👎</a>



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to