package superset import ( "bytes" "encoding/json" "io/ioutil" "net/http" "github.com/fiskerinc/cloud-services/pkg/httpclient" "github.com/fiskerinc/cloud-services/pkg/redis" "github.com/pkg/errors" ) var errUnauthorized = errors.New("superset unauthorized") type ( guestTokenRequest struct { User user `json:"user"` Resources []resource `json:"resources"` Rls []rule `json:"rls"` } user struct { UserName string `json:"username"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } resource struct { ID string `json:"id"` Type string `json:"type"` } rule struct { Clause string `json:"clause"` } guestTokenResponse struct { Token string `json:"token"` } ) func GetGuestToken(r redis.Client, accToken string) (string, error) { token, err := getGuestToken(accToken) if err == nil { return token, nil } if err != nil && !errors.Is(err, errUnauthorized) { return "", err } // if unauthorized accToken, err = loginFunc(r) if err != nil { return "", err } return getGuestToken(accToken) } func getGuestToken(accToken string) (string, error) { req, err := getGuestTokenReq(accToken) if err != nil { return "", err } resp, err := httpclient.Client.Do(req) if err != nil { return "", errors.WithStack(err) } defer resp.Body.Close() if resp.StatusCode == http.StatusUnauthorized { return "", errors.WithStack(errUnauthorized) } if resp.StatusCode != http.StatusOK { return "", errors.Errorf("superset guest token answered with status: %s", resp.Status) } body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", errors.WithStack(err) } var structResp guestTokenResponse err = json.Unmarshal(body, &structResp) if err != nil { return "", errors.WithStack(err) } return structResp.Token, nil } func getGuestTokenReq(accToken string) (*http.Request, error) { body, err := json.Marshal(guestTokenRequest{ User: user{ UserName: guestUserName, FirstName: guestFirstName, LastName: guestLastName, }, Resources: compileResources(accToken), Rls: []rule{}, }) if err != nil { return nil, errors.WithStack(err) } req, err := http.NewRequest(http.MethodPost, host+"/security/guest_token", bytes.NewReader(body)) if err != nil { return nil, errors.WithStack(err) } req.Header.Add("Authorization", "Bearer "+accToken) req.Header.Add("Content-Type", "application/json") return req, nil } func compileResources(accToken string) []resource { var res []resource dashes, err := GetEmbeddableDashboards(accToken) dashIDs := make([]string, 0) if err == nil { for _, d := range dashes { dashIDs = append(dashIDs, d.EmbeddingId) } } for _, id := range dashIDs { if id == "" { continue } res = append(res, resource{ ID: id, Type: "dashboard", }) } return res }