Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api /api/v1/dashboard and /api/v1/chart returning empty responses when get request is made using user with Admin role in Python #25890

Open
giacomochiarella opened this issue Nov 7, 2023 · 21 comments

Comments

@giacomochiarella
Copy link

giacomochiarella commented Nov 7, 2023

Dashboard api /api/v1/dashboard and /api/v1/chart returning empty responses when get request is made using user with Superset predefined Admin role in Python

Expected results

I expect to get all dashboards when user with Admin role uses /api/v1/dashboard
I expect to get all charts when user with Admin role uses /api/v1/chart

Actual results

I get
{"count":0,"description_columns":{},"ids":[],"label_columns":{"certification_details":"Certification Details","certified_by":"Certified By","changed_by.first_name":"Changed By First Name","changed_by.id":"Changed By Id","changed_by.last_name":"Changed By Last Name","changed_by_name":"Changed By Name","changed_on_delta_humanized":"Changed On Delta Humanized","changed_on_utc":"Changed On Utc","created_by.first_name":"Created By First Name","created_by.id":"Created By Id","created_by.last_name":"Created By Last Name","created_on_delta_humanized":"Created On Delta Humanized","css":"Css","dashboard_title":"Dashboard Title","id":"Id","is_managed_externally":"Is Managed Externally","json_metadata":"Json Metadata","owners.first_name":"Owners First Name","owners.id":"Owners Id","owners.last_name":"Owners Last Name","position_json":"Position Json","published":"Published","roles.id":"Roles Id","roles.name":"Roles Name","slug":"Slug","status":"Status","tags.id":"Tags Id","tags.name":"Tags Name","tags.type":"Tags Type","thumbnail_url":"Thumbnail Url","url":"Url"},"list_columns":["id","published","status","slug","url","css","position_json","json_metadata","thumbnail_url","certified_by","certification_details","changed_by.first_name","changed_by.last_name","changed_by.id","changed_by_name","changed_on_utc","changed_on_delta_humanized","created_on_delta_humanized","created_by.first_name","created_by.id","created_by.last_name","dashboard_title","owners.id","owners.first_name","owners.last_name","roles.id","roles.name","is_managed_externally","tags.id","tags.name","tags.type"],"list_title":"List Dashboard","order_columns":["changed_by.first_name","changed_on_delta_humanized","created_by.first_name","dashboard_title","published","changed_on"],"result":[]} on /api/v1/dashboard

I get
{"count":0,"description_columns":{},"ids":[],"label_columns":{"cache_timeout":"Cache Timeout","certification_details":"Certification Details","certified_by":"Certified By","changed_by.first_name":"Changed By First Name","changed_by.last_name":"Changed By Last Name","changed_by_name":"Changed By Name","changed_on_delta_humanized":"Changed On Delta Humanized","changed_on_dttm":"Changed On Dttm","changed_on_utc":"Changed On Utc","created_by.first_name":"Created By First Name","created_by.id":"Created By Id","created_by.last_name":"Created By Last Name","created_by_name":"Created By Name","created_on_delta_humanized":"Created On Delta Humanized","dashboards.dashboard_title":"Dashboards Dashboard Title","dashboards.id":"Dashboards Id","datasource_id":"Datasource Id","datasource_name_text":"Datasource Name Text","datasource_type":"Datasource Type","datasource_url":"Datasource Url","description":"Description","description_markeddown":"Description Markeddown","edit_url":"Edit Url","form_data":"Form Data","id":"Id","is_managed_externally":"Is Managed Externally","last_saved_at":"Last Saved At","last_saved_by.first_name":"Last Saved By First Name","last_saved_by.id":"Last Saved By Id","last_saved_by.last_name":"Last Saved By Last Name","owners.first_name":"Owners First Name","owners.id":"Owners Id","owners.last_name":"Owners Last Name","params":"Params","slice_name":"Slice Name","slice_url":"Slice Url","table.default_endpoint":"Table Default Endpoint","table.table_name":"Table Table Name","tags.id":"Tags Id","tags.name":"Tags Name","tags.type":"Tags Type","thumbnail_url":"Thumbnail Url","url":"Url","viz_type":"Viz Type"},"list_columns":["is_managed_externally","certified_by","certification_details","cache_timeout","changed_by.first_name","changed_by.last_name","changed_by_name","changed_on_delta_humanized","changed_on_dttm","changed_on_utc","created_by.first_name","created_by.id","created_by.last_name","created_by_name","created_on_delta_humanized","datasource_id","datasource_name_text","datasource_type","datasource_url","description","description_markeddown","edit_url","form_data","id","last_saved_at","last_saved_by.id","last_saved_by.first_name","last_saved_by.last_name","owners.first_name","owners.id","owners.last_name","dashboards.id","dashboards.dashboard_title","params","slice_name","slice_url","table.default_endpoint","table.table_name","thumbnail_url","url","viz_type","tags.id","tags.name","tags.type"],"list_title":"List Slice","order_columns":["changed_by.first_name","changed_on_delta_humanized","datasource_id","datasource_name","last_saved_at","last_saved_by.id","last_saved_by.first_name","last_saved_by.last_name","slice_name","viz_type"],"result":[]} on /api/v1/chart

I get 404 if I call /api/v1/dashboard/{id} on an existing dashboard (the id is read by accessing the dashboard and read the id in the url).

Calling /api/v1/log or /api/v1/dashboard return a 308 which is successfully redirected for /api/v1/log, instead it gives above response for /api/v1/dashboard

Environment

Running docker-compose.yml
Python 3.9.18
Flask 2.2.5
Werkzeug 2.3.3
Superset images: 3.1.0

DASHBOARD_RBAC: True

Checklist

I can use /api/v1/log without any issue in Python and in RESTClient
I can use /api/v1/dashboard in RESTCLient, when I call /api/v1/dashboard in Python I get 308 which is redirected but then I get the above response (no dashboards included)

@giacomochiarella giacomochiarella changed the title Dashboard api /api/v1/dashboard returning empty response when get request is made using user with Admin role Api /api/v1/dashboard and /api/v1/chart returning empty responses when get request is made using user with Admin role Nov 7, 2023
@giacomochiarella giacomochiarella changed the title Api /api/v1/dashboard and /api/v1/chart returning empty responses when get request is made using user with Admin role Api /api/v1/dashboard and /api/v1/chart returning empty responses when get request is made using user with Admin role in Python Nov 8, 2023
@gdowmont-gss
Copy link

gdowmont-gss commented Nov 16, 2023

I am having the same problem. Tried with curl and python requests and nothing returned.
dataset endpoint works fine.

Also checked and doing PUT to /api/v1/dashboard/{id} works fine so only GET doesn't get anything.

Using 3.0,1 image.

@gdowmont-gss
Copy link

gdowmont-gss commented Nov 16, 2023

I have just discovered that if you make a request and pass Cookie header with syntactically valid session cookie (you can get it from making the same request in a browser) I get expected response from /database and /chart endpoints.

Stange that they behave differently from /dataset


url = "http://localhost/api/v1/dashboard/"

payload = {}
headers = {
  'Accept': 'application/json',
  'Cookie': 'session=valid_cookie here',
  'Authorization': 'Bearer valid_token'
}

response = requests.request("GET", url, headers=headers, data=payload)

print(response.text)

@bronz3beard
Copy link

bronz3beard commented Jan 18, 2024

I have found that if I take the session=.cookie from my swagger and use that (hardcoded) in my api request it works (the dot before the cookie is intentional as it is not apart of the other cookie).

If I use the session=cookie returned from /api/v1/security/csrf_token/ it does not work, I am not sure what that means and I have no idea how to solve it right now, but being able to display the dashboards as a list is essential to any application that has embedded dashboards.

If I figure out how I can be apart of the solution or if I find a solution I will share it here ✌️ Also if anyone has some ideas of how I can get this endpoint to work /api/v1/dashboard/ I welcome all of them 😅.

code example:

import axios from 'axios';
import { SUPERSET_BASE } from '../../constants';
import { getCsrfToken, login } from './auth.helper';

export async function getDashboards() {
  const { accessToken } = await login();
  // const { sessionCookie, csrfToken } = await getCsrfToken(accessToken);

  const headers = {
    Accept: 'application/json',
    Authorization: `Bearer ${accessToken}`,
    // Cookie: `session=${sessionCookie}`, // This does not work 😑
    Cookie:
      'session=.session_cookie_copied_from_hosted_swagger_dev_tools' // this works...
  };

  const response = await axios.get(`${SUPERSET_BASE}/api/v1/dashboard/`, {
    headers
  });

  return response;
}

@bronz3beard
Copy link

bronz3beard commented Jan 18, 2024

The solution for this issue can be found here, I think this should be marked as resolved.
https://apache-superset.slack.com/archives/C01EP56QGTS/p1705521514606739?thread_ts=1704717577.433489&cid=C01EP56QGTS


Okay so as discussed below this is a fix for displaying the dashboard list only, if you try to view an embedded dashboard with the changes it will not work.

When getting the guest token there is a request to the /api/v1/me/roles/ endpoint which returns the Public role, even if you assign a different Role to the user requesting to view the embedded dashboard, the token is requested as a guest(Public Role), not as the assigned role, maybe after manually getting the roles of the signed in user (GET /api/v1/me/roles/), we should then be able to send them in the payload along with the rls?

For example; if the roles matrix is provided in the payload of the request, the token is granted with the provided roles, if not, it is granted with the default Public role.
This is a naive idea of a solution, I dont know the complexities of the python code. maybe it is related to this?
config.py line 345
fe-embedded

    const payload = {
      user: {
        username: 'js1',
        firstName: 'John',
        lastName: 'Smith'
      },
      resources: [
        {
          type: 'dashboard',
          id
        }
      ],
      roles: {
        gamma: [
          ['can_read', 'Chart'],
          ['can_read', 'Dashboard'],
          ['can_list', 'DynamicPlugin'],
          ['can_explore_json', 'Superset'],
          ...
        ]
      },
      rls: []
    };

@ss-ravi
Copy link

ss-ravi commented Jan 19, 2024

@bronz3beard I think the link you sent is not a solution to the problem.
It seems that having can read on Dashboard or can read on Chart in the public role breaks the dashboard/chart api.
We need a workaround in case we need these permissions on the public role.

@bronz3beard
Copy link

bronz3beard commented Jan 19, 2024

@ss-ravi Ahh okay I see, I thought the permission change to public was intentional, but it is just a temp fix not a solution. thanks for the extra context 🙌

@bronz3beard
Copy link

bronz3beard commented Jan 19, 2024

if I change this GUEST_ROLE_NAME = "Public" in the config.py on line 1561 toGUEST_ROLE_NAME = "Guest" and then create and give the Guest role the same permissions as Public role` everything is working as expected.

@cw1427
Copy link

cw1427 commented Jan 26, 2024

@bronz3beard I think the link you sent is not a solution to the problem. It seems that having can read on Dashboard or can read on Chart in the public role breaks the dashboard/chart api. We need a workaround in case we need these permissions on the public role.

Thanks to save my life. Just removed the Public role's perm and everything works.... Don't get it.

@giacomochiarella
Copy link
Author

giacomochiarella commented Mar 14, 2024

@cw1427 do you mean you just deleted the Public role?

@bronz3beard
Copy link

@cw1427 do you mean you just deleted the Public role?

You need to update the config to use GUEST (a role you create with exactly the same permissions as PUBLIC Role) rather than PUBLIC.

After you make that change to the config you could delete it if you want, up to you.

@giacomochiarella
Copy link
Author

giacomochiarella commented Mar 15, 2024

@bronz3beard thanks for the answer. It was not clear. What is the difference between PUBLIC_ROLE_LIKE and GUEST_ROLE_NAME? Right now I have setup PUBLIC_ROLE_LIKE to a custom role, can I assign the same role to GUEST_ROLE_NAME?

@bronz3beard
Copy link

@bronz3beard thanks for the answer. It was not clear. What is the difference between PUBLIC_ROLE_LIKE and GUEST_ROLE_NAME? Right now I have setup PUBLIC_ROLE_LIKE to a custom role, can I assign the same role to GUEST_ROLE_NAME?


My response was as clear as your question.

I dont know what your setup is, but from your question I would experiment with this solution, you suggested as a question.
PUBLIC_ROLE_LIKE to a custom role, can I assign the same role to GUEST_ROLE_NAME?

for me all I had to do was this to get ti to work, obviously this is a work around.


This is what my config looks like.

Screenshot 2024-03-15 at 2 34 32 pm

Screenshot 2024-03-15 at 2 35 07 pm

@giacomochiarella
Copy link
Author

giacomochiarella commented Mar 16, 2024

@bronz3beard unfortunately it does not work. I've setup PUBLIC_ROLE_LIKE = None and GUEST_ROLE_NAME = 'custom_role' but I still have the same issue. The only way it worked is by deleting Public role but it gets recreated if I restart Superset. Moreover, if I delete Public role I get Internal server error 500 if I just open Superset homepage and I'm not logged in (the error is name does not exist on None object or something, it tries to call .name on role object which is None, because Public does not exist).
I've also tried PUBLIC_ROLE_LIKE = None and GUEST_ROLE_NAME = 'custom_role' with custom_role equal to Public and nothing changed. The api still return no dashboards/charts even though the credentials have Admin role.
I've done these changes in superset_config.py

@giacomochiarella
Copy link
Author

any news here? It is crucial to be able use the api

@gruz
Copy link

gruz commented Jun 1, 2024

Check Public role permissions. If Dashboard can_read is set for Public role, then this happens. I'm not sure if it's intended behavior or bug.

@giacomochiarella
Copy link
Author

Check Public role permissions. If Dashboard can_read is set for Public role, then this happens. I'm not sure if it's intended behavior or bug.

@gruz could you be more specific on what you are suggesting to do? can_read on Dashboard is automatically assigned when you start Superset. What are the steps you are suggesting with your statement?

@gruz
Copy link

gruz commented Jun 2, 2024

@giacomochiarella In my case someone before me added permissions to the Public role. From scratch Public role doesn't have any permissions.

Among those permissions what can_read on Dashboard in Public role. After removing it from Public role I can get the list of Dashboards as expected (count to zero).

зображення

P.S. A guy who added the permissions to the Public role have told, at least some of them are needed for Dashboard embedding to work.

@zarkerus
Copy link

zarkerus commented Jul 28, 2024

Same problem here, we want to make a simple embedded dashboard from superset with a page listing all dashboard available, when click on one of the item it will redirect to the detail of that dashboard, so to make dashboard embedded work, we have to add

FEATURE_FLAGS = {
    "EMBEDDED_SUPERSET": True,
    "GUEST_TOKEN": True
}

~~PUBLIC_ROLE_LIKE = "Gamma" # <-- This one will add a bunch of permissions to Public role which is necessary to embedding dashboard to the site~~

But if Public Role have these permission then these API will no longer working as expected

GET: /api/v1/dashboard/ # <-- return count = 0
GET: /api/v1/dashboard/1/embedded # <-- return not found

If we delete public role then these 2 API will working as expected but then we will get internal server error in the embedded dashboard on our site.

If we use Cookie created when login from the browser then we can make these 2 APIs work as well but ofc we can't use that because that cookie will be delete as soon as we close the browser.

We used apache/superset:4.0.2 docker image, if anyone know a version where these APIs work as expected, please let me know

P/S: Nvm, I think I understand the problem now, the problem is, when we login as Guest through API, we can only see published dashboard not all dashboards. This is not a bug at all, hope this answer help others as well.

@jacob-roldan
Copy link

I think I found a workaround to use the API when the PUBLIC_ROLE_LIKE var is defined.

I’ve attached an example bash script with curl calls. The key is the method save_cookie_from_login. After get access and csrf tokens, the idea is to simulate to access to login html page and save the new cookie.
test_list_dashboards.zip

save_cookie_from_login () {
    # URL to send the request to
    local URL="${superset_url}/login/"

    local payload="csrf_token=${csrf_token}&username=admin&password=admin"

    # Do a POST request to login saving the new cookie
    local response2; response2=$(curl -b /tmp/cookies.txt -c /tmp/cookies.txt -s -X POST "$URL" -d "$payload")

}

I think this is could be a solution for these issues:
#25876
#25740

@giacomochiarella
Copy link
Author

giacomochiarella commented Aug 1, 2024

@jacob-roldan could you be more specific how to use your workaround in Python? I'm still having issues in calling /api/v1/database or database/{pk}, /api/v1/dataset or dataset/{pk}, /api/v1/chart or chart/{pk}, /api/v1/dashboard or dashboard/{pk}. They return 0 or 404

@jacob-roldan
Copy link

jacob-roldan commented Aug 1, 2024

A simple example using python to get all dashboards when user admin uses /api/v1/dashboard

test_list_dashboards.py.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants