Files
cloud-services/pkg/superset/guest_token.go

137 lines
2.8 KiB
Go

package superset
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"fiskerinc.com/modules/httpclient"
"fiskerinc.com/modules/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
}