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 }