access_token_provider.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. package pluginproxy
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "net/http"
  8. "net/url"
  9. "strconv"
  10. "sync"
  11. "time"
  12. "golang.org/x/oauth2"
  13. "github.com/grafana/grafana/pkg/models"
  14. "github.com/grafana/grafana/pkg/plugins"
  15. "golang.org/x/oauth2/jwt"
  16. )
  17. var (
  18. tokenCache = tokenCacheType{
  19. cache: map[string]*jwtToken{},
  20. }
  21. oauthJwtTokenCache = oauthJwtTokenCacheType{
  22. cache: map[string]*oauth2.Token{},
  23. }
  24. )
  25. type tokenCacheType struct {
  26. cache map[string]*jwtToken
  27. sync.Mutex
  28. }
  29. type oauthJwtTokenCacheType struct {
  30. cache map[string]*oauth2.Token
  31. sync.Mutex
  32. }
  33. type accessTokenProvider struct {
  34. route *plugins.AppPluginRoute
  35. datasourceId int64
  36. datasourceVersion int
  37. }
  38. type jwtToken struct {
  39. ExpiresOn time.Time `json:"-"`
  40. ExpiresOnString string `json:"expires_on"`
  41. AccessToken string `json:"access_token"`
  42. }
  43. func newAccessTokenProvider(ds *models.DataSource, pluginRoute *plugins.AppPluginRoute) *accessTokenProvider {
  44. return &accessTokenProvider{
  45. datasourceId: ds.Id,
  46. datasourceVersion: ds.Version,
  47. route: pluginRoute,
  48. }
  49. }
  50. func (provider *accessTokenProvider) getAccessToken(data templateData) (string, error) {
  51. tokenCache.Lock()
  52. defer tokenCache.Unlock()
  53. if cachedToken, found := tokenCache.cache[provider.getAccessTokenCacheKey()]; found {
  54. if cachedToken.ExpiresOn.After(time.Now().Add(time.Second * 10)) {
  55. logger.Info("Using token from cache")
  56. return cachedToken.AccessToken, nil
  57. }
  58. }
  59. urlInterpolated, err := interpolateString(provider.route.TokenAuth.Url, data)
  60. if err != nil {
  61. return "", err
  62. }
  63. params := make(url.Values)
  64. for key, value := range provider.route.TokenAuth.Params {
  65. interpolatedParam, err := interpolateString(value, data)
  66. if err != nil {
  67. return "", err
  68. }
  69. params.Add(key, interpolatedParam)
  70. }
  71. getTokenReq, _ := http.NewRequest("POST", urlInterpolated, bytes.NewBufferString(params.Encode()))
  72. getTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
  73. getTokenReq.Header.Add("Content-Length", strconv.Itoa(len(params.Encode())))
  74. resp, err := client.Do(getTokenReq)
  75. if err != nil {
  76. return "", err
  77. }
  78. defer resp.Body.Close()
  79. var token jwtToken
  80. if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
  81. return "", err
  82. }
  83. expiresOnEpoch, _ := strconv.ParseInt(token.ExpiresOnString, 10, 64)
  84. token.ExpiresOn = time.Unix(expiresOnEpoch, 0)
  85. tokenCache.cache[provider.getAccessTokenCacheKey()] = &token
  86. logger.Info("Got new access token", "ExpiresOn", token.ExpiresOn)
  87. return token.AccessToken, nil
  88. }
  89. func (provider *accessTokenProvider) getJwtAccessToken(ctx context.Context, data templateData) (string, error) {
  90. oauthJwtTokenCache.Lock()
  91. defer oauthJwtTokenCache.Unlock()
  92. if cachedToken, found := oauthJwtTokenCache.cache[provider.getAccessTokenCacheKey()]; found {
  93. if cachedToken.Expiry.After(time.Now().Add(time.Second * 10)) {
  94. logger.Debug("Using token from cache")
  95. return cachedToken.AccessToken, nil
  96. }
  97. }
  98. conf := &jwt.Config{}
  99. if val, ok := provider.route.JwtTokenAuth.Params["client_email"]; ok {
  100. interpolatedVal, err := interpolateString(val, data)
  101. if err != nil {
  102. return "", err
  103. }
  104. conf.Email = interpolatedVal
  105. }
  106. if val, ok := provider.route.JwtTokenAuth.Params["private_key"]; ok {
  107. interpolatedVal, err := interpolateString(val, data)
  108. if err != nil {
  109. return "", err
  110. }
  111. conf.PrivateKey = []byte(interpolatedVal)
  112. }
  113. if val, ok := provider.route.JwtTokenAuth.Params["token_uri"]; ok {
  114. interpolatedVal, err := interpolateString(val, data)
  115. if err != nil {
  116. return "", err
  117. }
  118. conf.TokenURL = interpolatedVal
  119. }
  120. conf.Scopes = provider.route.JwtTokenAuth.Scopes
  121. token, err := getTokenSource(conf, ctx)
  122. if err != nil {
  123. return "", err
  124. }
  125. oauthJwtTokenCache.cache[provider.getAccessTokenCacheKey()] = token
  126. logger.Info("Got new access token", "ExpiresOn", token.Expiry)
  127. return token.AccessToken, nil
  128. }
  129. var getTokenSource = func(conf *jwt.Config, ctx context.Context) (*oauth2.Token, error) {
  130. tokenSrc := conf.TokenSource(ctx)
  131. token, err := tokenSrc.Token()
  132. if err != nil {
  133. return nil, err
  134. }
  135. return token, nil
  136. }
  137. func (provider *accessTokenProvider) getAccessTokenCacheKey() string {
  138. return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceVersion, provider.route.Path, provider.route.Method)
  139. }