66 lines
1.8 KiB
Go
66 lines
1.8 KiB
Go
|
|
package main
|
||
|
|
|
||
|
|
import (
|
||
|
|
"encoding/json"
|
||
|
|
"errors"
|
||
|
|
"net/http"
|
||
|
|
"net/url"
|
||
|
|
"strings"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"github.com/grafana/authlib/authn"
|
||
|
|
"github.com/grafana/grafana-plugin-sdk-go/config"
|
||
|
|
)
|
||
|
|
|
||
|
|
type authorizer struct {
|
||
|
|
url string
|
||
|
|
client *http.Client
|
||
|
|
verifier authn.Verifier[struct{}]
|
||
|
|
}
|
||
|
|
|
||
|
|
func newAuthorizer(base string) *authorizer {
|
||
|
|
base = strings.TrimRight(base, "/")
|
||
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
||
|
|
keys := authn.NewKeyRetriever(authn.KeyRetrieverConfig{SigningKeysURL: base + "/api/signing-keys/keys"}, authn.WithHTTPClientKeyRetrieverOpt(client))
|
||
|
|
return &authorizer{
|
||
|
|
url: base,
|
||
|
|
client: client,
|
||
|
|
verifier: authn.NewVerifier[struct{}](authn.VerifierConfig{}, authn.TokenTypeID, keys),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func (a *authorizer) canSubmit(req *http.Request) (bool, error) {
|
||
|
|
cfg := config.GrafanaConfigFromContext(req.Context())
|
||
|
|
token, err := cfg.PluginAppClientSecret()
|
||
|
|
if err != nil || token == "" {
|
||
|
|
return false, errors.New("plugin service account is unavailable")
|
||
|
|
}
|
||
|
|
claims, err := a.verifier.Verify(req.Context(), req.Header.Get("X-Grafana-Id"))
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
id, ok := strings.CutPrefix(claims.Subject, "user:")
|
||
|
|
if !ok || id == "" {
|
||
|
|
return false, nil
|
||
|
|
}
|
||
|
|
request, err := http.NewRequestWithContext(req.Context(), "GET", a.url+"/api/users/"+url.PathEscape(id), nil)
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
request.Header.Set("Authorization", "Bearer "+token)
|
||
|
|
response, err := a.client.Do(request)
|
||
|
|
if err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
defer response.Body.Close()
|
||
|
|
if response.StatusCode != http.StatusOK {
|
||
|
|
return false, errors.New("user lookup is unavailable")
|
||
|
|
}
|
||
|
|
var profile struct {
|
||
|
|
Admin bool `json:"isGrafanaAdmin"`
|
||
|
|
}
|
||
|
|
if err := json.NewDecoder(response.Body).Decode(&profile); err != nil {
|
||
|
|
return false, err
|
||
|
|
}
|
||
|
|
return profile.Admin, nil
|
||
|
|
}
|