sig-rnd-io-testuser opened a new issue, #43257:
URL: https://github.com/apache/superset/issues/43257
### Bug description
Superset 6.1.0 added `@has_access_api` decorator to the `post()` and `put()`
methods of `DashboardFilterStateRestApi` (in
`superset/dashboards/filter_state/api.py`). This causes **401 "Access is
Denied"** for all users, **including admin**, when calling `POST
/api/v1/dashboard/{id}/filter_state` or `PUT
/api/v1/dashboard/{id}/filter_state/{key}`.
GET and DELETE on the same endpoint work fine because they do not have
`@has_access_api`.
### Root cause
The decorator ordering on POST/PUT is:
```python
@api
@has_access_api # runs BEFORE @protect()
@expose("/<int:pk>/filter_state", methods=("POST",))
@protect() # never reached
@safe
@event_logger.log_this_with_context(...)
def post(self, pk: int) -> Response:
```
`@has_access_api` checks `current_user` via Flask-Login. However,
`@protect()` (which validates JWT Bearer tokens and sets `current_user`) runs
**after** `@has_access_api` in the decorator chain. The session cookie from the
CSRF token endpoint only contains `{"csrf_token": "..."}` — no user identity.
So `current_user` is anonymous when `@has_access_api` evaluates, and it returns
`response_401()`.
This was not an issue in Superset 4.1.1 because `@has_access_api` was not
present on these methods — only `@protect()` was used.
### Comparison: 4.1.1 vs 6.1.0
| Version | POST/PUT decorators | Result |
|---------|-------------------|--------|
| 4.1.1 | `@expose`, `@protect()`, `@safe`, `@event_logger` | Works |
| 6.1.0 | `@api`, `@has_access_api`, `@expose`, `@protect()`, `@safe`,
`@event_logger` | 401 |
### How to reproduce
1. Deploy Superset 6.1.0
2. Login via API:
```bash
curl -s -X POST http://localhost:8088/api/v1/security/login \
-H "Content-Type: application/json" \
-d
'{"username":"admin","password":"admin","provider":"db","refresh":"true"}'
```
3. Get CSRF token:
```bash
curl -sv http://localhost:8088/api/v1/security/csrf_token/ \
-H "Authorization: Bearer <access_token>"
```
4. POST to filter_state:
```bash
curl -X POST "http://localhost:8088/api/v1/dashboard/1/filter_state" \
-H "Authorization: Bearer <access_token>" \
-H "X-CSRFToken: <csrf_token>" \
-H "Cookie: <session_cookie>" \
-H "Content-Type: application/json" \
-d '{"value":"{\"test\":\"test\"}"}'
```
5. Response: `{"message":"Access is Denied","severity":"danger"}` with HTTP
401
6. Compare with GET (which does NOT have `@has_access_api`):
```bash
curl "http://localhost:8088/api/v1/dashboard/1/filter_state/any_key" \
-H "Authorization: Bearer <access_token>" \
-H "Cookie: <session_cookie>"
```
Response: `{"message":"Not found"}` with HTTP 404 — auth passes, key just
doesn't exist.
### Expected results
POST and PUT to `/api/v1/dashboard/{id}/filter_state` should succeed with
valid JWT + CSRF tokens (as they did in 4.1.1).
### Actual results
401 "Access is Denied" for all users including admin.
### Suggested fix
Either:
1. **Remove `@has_access_api`** from `post()` and `put()` in
`DashboardFilterStateRestApi`, reverting to the 4.1.1 decorator chain where
`@protect()` handles authentication.
2. **Or reorder** so `@protect()` runs before `@has_access_api`, ensuring
`current_user` is set from the JWT token before the permission check.
### Superset version
6.1.0
### Python version
3.12
### Node version
22.x
### Browser
Not applicable (API-only issue)
### Additional context
This also likely affects `ExploreFormDataRestApi` and
`ExplorePermalinkRestApi` which have the same decorator pattern in 6.1.0.
Workaround: patch the Docker image to comment out `@has_access_api` via sed.
--
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]