|
|
@@ -2,6 +2,7 @@ package pluginproxy
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"html/template"
|
|
|
@@ -10,6 +11,7 @@ import (
|
|
|
"net/http"
|
|
|
"net/http/httputil"
|
|
|
"net/url"
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
|
|
|
@@ -23,9 +25,19 @@ import (
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
- logger log.Logger = log.New("data-proxy-log")
|
|
|
+ logger log.Logger = log.New("data-proxy-log")
|
|
|
+ client *http.Client = &http.Client{
|
|
|
+ Timeout: time.Second * 30,
|
|
|
+ Transport: &http.Transport{Proxy: http.ProxyFromEnvironment},
|
|
|
+ }
|
|
|
)
|
|
|
|
|
|
+type jwtToken struct {
|
|
|
+ ExpiresOn time.Time `json:"-"`
|
|
|
+ ExpiresOnString string `json:"expires_on"`
|
|
|
+ AccessToken string `json:"access_token"`
|
|
|
+}
|
|
|
+
|
|
|
type DataSourceProxy struct {
|
|
|
ds *m.DataSource
|
|
|
ctx *middleware.Context
|
|
|
@@ -229,8 +241,6 @@ func checkWhiteList(c *middleware.Context, host string) bool {
|
|
|
}
|
|
|
|
|
|
func (proxy *DataSourceProxy) applyRoute(req *http.Request) {
|
|
|
- logger.Info("ApplyDataSourceRouteRules", "route", proxy.route.Path, "proxyPath", proxy.proxyPath)
|
|
|
-
|
|
|
proxy.proxyPath = strings.TrimPrefix(proxy.proxyPath, proxy.route.Path)
|
|
|
|
|
|
data := templateData{
|
|
|
@@ -238,8 +248,6 @@ func (proxy *DataSourceProxy) applyRoute(req *http.Request) {
|
|
|
SecureJsonData: proxy.ds.SecureJsonData.Decrypt(),
|
|
|
}
|
|
|
|
|
|
- logger.Info("Apply Route Rule", "rule", proxy.route.Path)
|
|
|
-
|
|
|
routeUrl, err := url.Parse(proxy.route.Url)
|
|
|
if err != nil {
|
|
|
logger.Error("Error parsing plugin route url")
|
|
|
@@ -254,25 +262,80 @@ func (proxy *DataSourceProxy) applyRoute(req *http.Request) {
|
|
|
if err := addHeaders(&req.Header, proxy.route, data); err != nil {
|
|
|
logger.Error("Failed to render plugin headers", "error", err)
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-func addHeaders(reqHeaders *http.Header, route *plugins.AppPluginRoute, data templateData) error {
|
|
|
- for _, header := range route.Headers {
|
|
|
- var contentBuf bytes.Buffer
|
|
|
- t, err := template.New("content").Parse(header.Content)
|
|
|
- if err != nil {
|
|
|
- return errors.New(fmt.Sprintf("could not parse header content template for header %s.", header.Name))
|
|
|
+ if proxy.route.TokenAuth != nil {
|
|
|
+ if token, err := proxy.getAccessToken(data); err != nil {
|
|
|
+ logger.Error("Failed to get access token", "error", err)
|
|
|
+ } else {
|
|
|
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
|
|
|
}
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- err = t.Execute(&contentBuf, data)
|
|
|
- if err != nil {
|
|
|
- return errors.New(fmt.Sprintf("failed to execute header content template for header %s.", header.Name))
|
|
|
+func (proxy *DataSourceProxy) getAccessToken(data templateData) (string, error) {
|
|
|
+ urlInterpolated, err := interpolateString(proxy.route.TokenAuth.Url, data)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ logger.Info("client secret", "ClientSecret", data.SecureJsonData["clientSecret"])
|
|
|
+ params := make(url.Values)
|
|
|
+ for key, value := range proxy.route.TokenAuth.Params {
|
|
|
+ if interpolatedParam, err := interpolateString(value, data); err != nil {
|
|
|
+ return "", err
|
|
|
+ } else {
|
|
|
+ logger.Info("param", key, interpolatedParam)
|
|
|
+ params.Add(key, interpolatedParam)
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- value := contentBuf.String()
|
|
|
+ getTokenReq, _ := http.NewRequest("POST", urlInterpolated, bytes.NewBufferString(params.Encode()))
|
|
|
+ getTokenReq.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
|
+ getTokenReq.Header.Add("Content-Length", strconv.Itoa(len(params.Encode())))
|
|
|
|
|
|
- logger.Info("Adding headers", "name", header.Name, "value", value)
|
|
|
- reqHeaders.Add(header.Name, value)
|
|
|
+ resp, err := client.Do(getTokenReq)
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ defer resp.Body.Close()
|
|
|
+ respData, err := ioutil.ReadAll(resp.Body)
|
|
|
+ logger.Info("Resp", "resp", string(respData))
|
|
|
+
|
|
|
+ var token jwtToken
|
|
|
+ if err := json.NewDecoder(resp.Body).Decode(&token); err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+
|
|
|
+ expiresOnEpoch, _ := strconv.ParseInt(token.ExpiresOnString, 10, 64)
|
|
|
+ token.ExpiresOn = time.Unix(expiresOnEpoch, 0)
|
|
|
+
|
|
|
+ logger.Debug("Got new access token", "ExpiresOn", token.ExpiresOn)
|
|
|
+ return "", nil
|
|
|
+}
|
|
|
+
|
|
|
+func interpolateString(text string, data templateData) (string, error) {
|
|
|
+ t, err := template.New("content").Parse(text)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.New(fmt.Sprintf("Could not parse template %s.", text))
|
|
|
+ }
|
|
|
+
|
|
|
+ var contentBuf bytes.Buffer
|
|
|
+ err = t.Execute(&contentBuf, data)
|
|
|
+ if err != nil {
|
|
|
+ return "", errors.New(fmt.Sprintf("Failed to execute template %s.", text))
|
|
|
+ }
|
|
|
+
|
|
|
+ return contentBuf.String(), nil
|
|
|
+}
|
|
|
+
|
|
|
+func addHeaders(reqHeaders *http.Header, route *plugins.AppPluginRoute, data templateData) error {
|
|
|
+ for _, header := range route.Headers {
|
|
|
+ interpolated, err := interpolateString(header.Content, data)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ reqHeaders.Add(header.Name, interpolated)
|
|
|
}
|
|
|
|
|
|
return nil
|