PluginProxy: Split implementations of token providers (#32820)
* Split implementations of token providers * Fix imports * Fix code racing in unit tests
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
package pluginproxy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
)
|
||||
|
||||
var (
|
||||
tokenCache = tokenCacheType{
|
||||
cache: map[string]*jwtToken{},
|
||||
}
|
||||
)
|
||||
|
||||
type tokenCacheType struct {
|
||||
cache map[string]*jwtToken
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type genericAccessTokenProvider struct {
|
||||
datasourceId int64
|
||||
datasourceVersion int
|
||||
route *plugins.AppPluginRoute
|
||||
data templateData
|
||||
}
|
||||
|
||||
type jwtToken struct {
|
||||
ExpiresOn time.Time
|
||||
AccessToken string
|
||||
}
|
||||
|
||||
func (token *jwtToken) UnmarshalJSON(b []byte) error {
|
||||
var t struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresOn *json.Number `json:"expires_on"`
|
||||
ExpiresIn *json.Number `json:"expires_in"`
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(b, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
token.AccessToken = t.AccessToken
|
||||
token.ExpiresOn = timeNow()
|
||||
|
||||
if t.ExpiresOn != nil {
|
||||
expiresOn, err := t.ExpiresOn.Int64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.ExpiresOn = time.Unix(expiresOn, 0)
|
||||
} else if t.ExpiresIn != nil {
|
||||
expiresIn, err := t.ExpiresIn.Int64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
token.ExpiresOn = timeNow().Add(time.Duration(expiresIn) * time.Second)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newGenericAccessTokenProvider(ds *models.DataSource, pluginRoute *plugins.AppPluginRoute,
|
||||
data templateData) *genericAccessTokenProvider {
|
||||
return &genericAccessTokenProvider{
|
||||
datasourceId: ds.Id,
|
||||
datasourceVersion: ds.Version,
|
||||
route: pluginRoute,
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *genericAccessTokenProvider) getAccessToken() (string, error) {
|
||||
tokenCache.Lock()
|
||||
defer tokenCache.Unlock()
|
||||
if cachedToken, found := tokenCache.cache[provider.getAccessTokenCacheKey()]; found {
|
||||
if cachedToken.ExpiresOn.After(timeNow().Add(time.Second * 10)) {
|
||||
logger.Info("Using token from cache")
|
||||
return cachedToken.AccessToken, nil
|
||||
}
|
||||
}
|
||||
|
||||
urlInterpolated, err := interpolateString(provider.route.TokenAuth.Url, provider.data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
params := make(url.Values)
|
||||
for key, value := range provider.route.TokenAuth.Params {
|
||||
interpolatedParam, err := interpolateString(value, provider.data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
params.Add(key, interpolatedParam)
|
||||
}
|
||||
|
||||
getTokenReq, err := http.NewRequest("POST", urlInterpolated, bytes.NewBufferString(params.Encode()))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
getTokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
getTokenReq.Header.Set("Content-Length", strconv.Itoa(len(params.Encode())))
|
||||
|
||||
resp, err := client.Do(getTokenReq)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
logger.Warn("Failed to close response body", "err", err)
|
||||
}
|
||||
}()
|
||||
|
||||
var token jwtToken
|
||||
if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tokenCache.cache[provider.getAccessTokenCacheKey()] = &token
|
||||
logger.Info("Got new access token", "ExpiresOn", token.ExpiresOn)
|
||||
return token.AccessToken, nil
|
||||
}
|
||||
|
||||
func (provider *genericAccessTokenProvider) getAccessTokenCacheKey() string {
|
||||
return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceVersion, provider.route.Path, provider.route.Method)
|
||||
}
|
||||
Reference in New Issue
Block a user