| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | package pluginproxy | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2018-09-14 17:13:09 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2018-09-14 17:13:09 +08:00
										 |  |  | 	tokenCache = tokenCacheType{ | 
					
						
							|  |  |  | 		cache: map[string]*jwtToken{}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-14 17:13:09 +08:00
										 |  |  | type tokenCacheType struct { | 
					
						
							|  |  |  | 	cache map[string]*jwtToken | 
					
						
							|  |  |  | 	sync.Mutex | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-03 20:46:32 +08:00
										 |  |  | type genericAccessTokenProvider struct { | 
					
						
							| 
									
										
										
										
											2018-09-21 20:24:44 +08:00
										 |  |  | 	datasourceId      int64 | 
					
						
							| 
									
										
										
										
											2021-10-08 20:46:35 +08:00
										 |  |  | 	datasourceUpdated time.Time | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	route             *plugins.Route | 
					
						
							|  |  |  | 	authParams        *plugins.JWTTokenAuth | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type jwtToken struct { | 
					
						
							| 
									
										
										
										
											2020-02-26 22:59:02 +08:00
										 |  |  | 	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 | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | func newGenericAccessTokenProvider(ds DSInfo, pluginRoute *plugins.Route, | 
					
						
							|  |  |  | 	authParams *plugins.JWTTokenAuth) *genericAccessTokenProvider { | 
					
						
							| 
									
										
										
										
											2021-05-03 20:46:32 +08:00
										 |  |  | 	return &genericAccessTokenProvider{ | 
					
						
							| 
									
										
										
										
											2021-10-08 20:46:35 +08:00
										 |  |  | 		datasourceId:      ds.ID, | 
					
						
							|  |  |  | 		datasourceUpdated: ds.Updated, | 
					
						
							| 
									
										
										
										
											2018-09-21 20:24:44 +08:00
										 |  |  | 		route:             pluginRoute, | 
					
						
							| 
									
										
										
										
											2021-05-07 04:05:23 +08:00
										 |  |  | 		authParams:        authParams, | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-11 23:02:24 +08:00
										 |  |  | func (provider *genericAccessTokenProvider) GetAccessToken() (string, error) { | 
					
						
							| 
									
										
										
										
											2018-09-14 17:13:09 +08:00
										 |  |  | 	tokenCache.Lock() | 
					
						
							|  |  |  | 	defer tokenCache.Unlock() | 
					
						
							|  |  |  | 	if cachedToken, found := tokenCache.cache[provider.getAccessTokenCacheKey()]; found { | 
					
						
							| 
									
										
										
										
											2020-02-26 22:59:02 +08:00
										 |  |  | 		if cachedToken.ExpiresOn.After(timeNow().Add(time.Second * 10)) { | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 			logger.Info("Using token from cache") | 
					
						
							|  |  |  | 			return cachedToken.AccessToken, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 04:05:23 +08:00
										 |  |  | 	tokenUrl := provider.authParams.Url | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	params := make(url.Values) | 
					
						
							| 
									
										
										
										
											2021-05-07 04:05:23 +08:00
										 |  |  | 	for key, value := range provider.authParams.Params { | 
					
						
							|  |  |  | 		params.Add(key, value) | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-07 04:05:23 +08:00
										 |  |  | 	getTokenReq, err := http.NewRequest("POST", tokenUrl, bytes.NewBufferString(params.Encode())) | 
					
						
							| 
									
										
										
										
											2020-12-15 16:32:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-14 22:13:01 +08:00
										 |  |  | 	getTokenReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") | 
					
						
							|  |  |  | 	getTokenReq.Header.Set("Content-Length", strconv.Itoa(len(params.Encode()))) | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	resp, err := client.Do(getTokenReq) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-15 16:32:06 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if err := resp.Body.Close(); err != nil { | 
					
						
							|  |  |  | 			logger.Warn("Failed to close response body", "err", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var token jwtToken | 
					
						
							|  |  |  | 	if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-14 17:13:09 +08:00
										 |  |  | 	tokenCache.cache[provider.getAccessTokenCacheKey()] = &token | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | 	logger.Info("Got new access token", "ExpiresOn", token.ExpiresOn) | 
					
						
							|  |  |  | 	return token.AccessToken, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-03 20:46:32 +08:00
										 |  |  | func (provider *genericAccessTokenProvider) getAccessTokenCacheKey() string { | 
					
						
							| 
									
										
										
										
											2021-10-08 20:46:35 +08:00
										 |  |  | 	return fmt.Sprintf("%v_%v_%v_%v", provider.datasourceId, provider.datasourceUpdated.Unix(), provider.route.Path, provider.route.Method) | 
					
						
							| 
									
										
										
										
											2018-09-06 21:50:16 +08:00
										 |  |  | } |