FynnMazurkiewicz commented on issue #24837:
URL: https://github.com/apache/superset/issues/24837#issuecomment-2501991590

   TLDR: **Patch available!**
   
   This bug is still present in version 4.0.1. The workaround suggested by 
@lbuchli did not work for me either.
   
   **Bug summary**
   Whenever you set the "can_read" permission of "Dashboard" as part of the 
public permissions, the API of that endpoint will start ignoring the JWT 
entirely and always assume that the public user makes the request. In fact, 
this bug not only occurs for this endpoint, but for all endpoints. As soon as 
any item is part of the public read permissions, the JWT is ignored. This 
breaks the JWT authorization, as the API ignores any given token and always 
assumes public user permissions. For example, the dashboard endpoint will 
always return only dashboards available to the public user, even if you supply 
a JWT Bearer token of a user that has access to more dashboards (such as the 
admin role).
   
   **Root cause**
   I have completed a root cause analysis of this bug. It is caused by an issue 
in FlaskAppBuilder, the underlying framework also built by the lovely 
@dpgaspar. [In the security decorator 
`_protect()`](https://github.com/dpgaspar/Flask-AppBuilder/blob/master/flask_appbuilder/security/decorators.py#L98),
 that is called to set the authorization context on any endpoint, there is a 
special condition set for items that are considered public (meaning the public 
permissions can read them). I'll quote the relevant parts for discussion:
   
   ```python
   flask_appbuilder/security/decorators.py:98
   
   # Check if the resource is public
   if current_app.appbuilder.sm.is_item_public(permission_str, 
class_permission_name):
       return f(self, *args, **kwargs)
   
   # if no browser login then verify JWT
   if not (self.allow_browser_login or allow_browser_login):
       verify_jwt_in_request()
   
   # Verify resource access
   if current_app.appbuilder.sm.has_access(permission_str, 
class_permission_name):
       return f(self, *args, **kwargs)
   ```
   In this decorator, it is first checked whether the item is public and if it 
is, the endpoint function is immediately called - with no further JWT token 
handling. You can see the function call to `verify_jwt_in_request()` is only 
called when the public condition check fails.
   
   I am not sure if this can be considered a bug in FAB itself, so I won't open 
a bug issue there. I do see however, that this might cause issues a lot and I 
highly question this order is correct. @dpgaspar What do you think?
   
   **Patch**
   Until there is a proper fix implemented by @dpgaspar or any other 
maintainer, its possible to hack around this by attempting a JWT login by 
overwriting `is_item_public` in the SecurityManager and adding a call to 
`verify_jwt_in_request` there.
   ```python
   class MySecurityManager(SupersetSecurityManager):
       ...
       def is_item_public(self, permission_name, view_name):
           verify_jwt_in_request(optional=True) # Attempt to parse any existing 
JWT and fail silently
           return super().is_item_public(permission_name, view_name)
       ...
   ```
   Thanks to my employer https://www.dksr.city/en/ for donating A LOT of time 
to work on this and being able to donate my findings back to the open source 
community.


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