apache / superset

Apache Superset is a Data Visualization and Data Exploration Platform
https://superset.apache.org/
Apache License 2.0
61.76k stars 13.5k forks source link

Method 405 not allowed for guest_token fetching #28777

Open stockarea opened 3 months ago

stockarea commented 3 months ago

Bug description

I am not able to fetch the guest_token. I am facing the same issue as described here in this issue

23060

I am also getting the access_token, but I am not able to get guest_token afterwards (405 method not allowed) I have nginx proxy server in front of my superset which is passing the request from port 443 to 8088. I am still getting this issue.

How to reproduce the bug

..

Screenshots/recordings

No response

Superset version

master

Python version

3.9

Node version

16

Browser

Chrome

Additional context

No response

Checklist

dosubot[bot] commented 3 months ago

Hey @stockarea! 👋 I'm here to help you with any bugs, questions, or contributions you have for the repository. Let me know how I can assist you!

To resolve the "Method 405 not allowed" error when trying to fetch the guest_token in Superset version 4.0.1, ensure that you are making a POST request to the /guest_token/ endpoint. The endpoint is explicitly defined to accept only POST requests:

@expose("/guest_token/", methods=("POST",))

Here is an example of how to make a correct POST request to this endpoint:

curl -X POST https://your-superset-domain.com/guest_token/ \
     -H "Content-Type: application/json" \
     -d '{
           "user": {
             "username": "guest_user",
             "first_name": "Guest",
             "last_name": "User"
           },
           "resources": [
             {
               "type": "dashboard",
               "id": "1"
             }
           ],
           "rls": []
         }'

Ensure that your nginx proxy server is correctly forwarding POST requests from port 443 to port 8088. Here is a sample nginx configuration snippet:

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent [$connection_requests] "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    keepalive_timeout  30;
    keepalive_requests 2;

    gzip on;
    gzip_comp_level    5;
    gzip_min_length    256;
    gzip_proxied       any;
    gzip_vary          on;
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/rss+xml
        application/vnd.ms-fontobject
        application/x-font-ttf
        application/x-web-app-manifest+json
        application/xhtml+xml
        application/xml
        font/opentype
        image/svg+xml
        image/x-icon
        text/css
        text/plain
        text/x-component;

    output_buffers 20 10m;
    client_max_body_size 10m;

    upstream superset_app {
        server host.docker.internal:8088;
        keepalive 100;
    }

    upstream superset_websocket {
        server host.docker.internal:8080;
        keepalive 100;
    }

    server {
        listen 443 ssl;
        server_name your-superset-domain.com;

        location /ws {
            proxy_pass http://superset_websocket;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "Upgrade";
            proxy_set_header Host $host;
        }

        location / {
            proxy_pass http://superset_app;
            proxy_set_header    Host                $host;
            proxy_set_header    X-Real-IP           $remote_addr;
            proxy_set_header    X-Forwarded-For     $remote_addr;
            proxy_set_header    X-Forwarded-Host    $host;
            proxy_set_header    X-Forwarded-Proto   $scheme;
            proxy_http_version 1.1;
            port_in_redirect off;
            proxy_connect_timeout 300;
        }
    }
}

Make sure that the proxy_pass directive is correctly set to forward requests to the Superset backend running on port 8088 [1][2].

To continue talking to Dosu, mention @dosu.

stockarea commented 3 months ago

Hello, I am passing POST request only. Here is the screenshot for it.

image

Also, nginx is working correctly I suppose that's why I am able to fetch the access_token it seems? Otherwise that API will also fail correct?

dosubot[bot] commented 3 months ago

If you're already making a POST request and your nginx configuration is correctly forwarding requests, the issue might be related to how the endpoint is defined or how the request is being handled by Superset.

  1. Ensure Endpoint Definition: Verify that the /guest_token/ endpoint is defined to accept POST requests in your Superset configuration. The endpoint should be defined like this:

    @expose("/guest_token/", methods=("POST",))
    def guest_token(self) -> FlaskResponse:
        # Endpoint logic here
  2. Nginx Configuration: Double-check your nginx configuration to ensure it allows POST requests to the /guest_token/ endpoint. Here is a sample configuration snippet:

    server {
        listen 443 ssl;
        server_name your-superset-domain.com;
    
        location / {
            proxy_pass http://superset_app;
            proxy_set_header    Host                $host;
            proxy_set_header    X-Real-IP           $remote_addr;
            proxy_set_header    X-Forwarded-For     $remote_addr;
            proxy_set_header    X-Forwarded-Host    $host;
            proxy_set_header    X-Forwarded-Proto   $scheme;
            proxy_http_version 1.1;
            port_in_redirect off;
            proxy_connect_timeout 300;
        }
    
        location /guest_token/ {
            proxy_pass http://superset_app/guest_token/;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_http_version 1.1;
            proxy_method POST;  # Ensure POST method is allowed
        }
    }
  3. Superset Client Configuration: Ensure that your Superset client is correctly configured to handle the guest_token. The SupersetClientClass should include the guestToken and guestTokenHeaderName properties, and the post method should be used to make the POST request. Here is a snippet from the SupersetClientClass:

    export default class SupersetClientClass {
      guestToken?: string;
      guestTokenHeaderName: string;
    
      constructor({
        guestToken = undefined,
        guestTokenHeaderName = 'X-GuestToken',
      }: ClientConfig = {}) {
        this.guestToken = guestToken;
        this.guestTokenHeaderName = guestTokenHeaderName;
        if (guestToken) {
          this.headers[guestTokenHeaderName] = guestToken;
        }
      }
    
      async post<T extends ParseMethod = 'json'>(
        requestConfig: RequestConfig & { parseMethod?: T },
      ) {
        return this.request({ ...requestConfig, method: 'POST' });
      }
    }
  4. Reload Nginx: After making changes to your nginx configuration, reload or restart the nginx service to apply the changes:

    sudo nginx -s reload

By ensuring that the endpoint is correctly defined, the nginx configuration allows POST requests, and the Superset client is properly configured, you should be able to resolve the "Method 405 not allowed" error when fetching the guest_token [1][2][3].

To continue talking to Dosu, mention @dosu.

zarkerus commented 3 months ago

I got the same error, this is the stack trace, the application got "POST /api/v1/security/guest_token HTTP/1.1" 308 293 " correctly at first but somehow ended up with the error.

10.0.2.40 - - [01/Jun/2024:12:31:57 +0000] "POST /api/v1/security/guest_token HTTP/1.1" 308 293 "-" "PostmanRuntime/7.39.0"
HTTPException
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1788, in dispatch_request
    self.raise_routing_exception(req)
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1770, in raise_routing_exception
    raise request.routing_exception  # type: ignore
  File "/usr/local/lib/python3.9/site-packages/flask/ctx.py", line 351, in match_request
    result = self.url_adapter.match(return_rule=True)  # type: ignore
  File "/usr/local/lib/python3.9/site-packages/werkzeug/routing/map.py", line 647, in match
    raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None
werkzeug.exceptions.MethodNotAllowed: 405 Method Not Allowed: The method is not allowed for the requested URL.
2024-06-01 12:31:58,099:WARNING:superset.views.base:HTTPException
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1823, in full_dispatch_request
    rv = self.dispatch_request()
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1788, in dispatch_request
    self.raise_routing_exception(req)
  File "/usr/local/lib/python3.9/site-packages/flask/app.py", line 1770, in raise_routing_exception
    raise request.routing_exception  # type: ignore
  File "/usr/local/lib/python3.9/site-packages/flask/ctx.py", line 351, in match_request
    result = self.url_adapter.match(return_rule=True)  # type: ignore
  File "/usr/local/lib/python3.9/site-packages/werkzeug/routing/map.py", line 647, in match
    raise MethodNotAllowed(valid_methods=list(e.have_match_for)) from None
werkzeug.exceptions.MethodNotAllowed: 405 Method Not Allowed: The method is not allowed for the requested URL.
10.0.2.40 - - [01/Jun/2024:12:31:58 +0000] "GET /api/v1/security/guest_token/ HTTP/1.1" 405 271 "http://mydashboard.com/api/v1/security/guest_token/" "PostmanRuntime/7.39.0"

And I already had nginx proxy my request to superset-server:8088 Also this is how my superset_config_docker.py look like

PUBLIC_ROLE_LIKE = "Gamma"

TALISMAN_ENABLED = False

ENABLE_CORS = True

HTTP_HEADERS = {
    "X-Frame-Options": "ALLOWALL"
}

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

And this is my nginx.conf

    location / {
        proxy_set_header   Host $http_host;
        proxy_pass             http://dashboard;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection "upgrade";
    }

    listen 443 ssl;

Updated: I tried to use curl to post to superset directly and got this result

<!doctype html>
<html lang=en>
<title>Redirecting...</title>
<h1>Redirecting...</h1>
<p>You should be redirected automatically to the target URL: <a href="http://ip-address:8088/api/v1/security/guest_token/">http://ip-address:8088/api/v1/security/guest_token/</a>. If not, click the link.

Look like a POST to api/v1/security/guest_token result in a redirect to GET api/v1/security/guest_token, that's why I got this error

zarkerus commented 3 months ago

So this is the fix

rusackas commented 3 months ago

@stockarea let us know if this indeed resolves the issue for you. 🤞