Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
N
nexpie-grafana-theme
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Registry
Registry
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Kornkitt Poolsup
nexpie-grafana-theme
Commits
3c9798be
Commit
3c9798be
authored
Aug 23, 2017
by
Torkel Ödegaard
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
datasource-proxy: token exchange
parent
4f9fbcc2
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
130 additions
and
33 deletions
+130
-33
pkg/api/avatar/avatar.go
+4
-1
pkg/api/pluginproxy/ds_proxy.go
+81
-18
pkg/api/pluginproxy/ds_proxy_test.go
+13
-0
pkg/plugins/app_plugin.go
+11
-5
tests/datasource-test/module.js
+17
-5
tests/datasource-test/plugin.json
+4
-4
No files found.
pkg/api/avatar/avatar.go
View file @
3c9798be
...
...
@@ -217,7 +217,10 @@ func (this *thunderTask) Fetch() {
this
.
Done
()
}
var
client
=
&
http
.
Client
{}
var
client
*
http
.
Client
=
&
http
.
Client
{
Timeout
:
time
.
Second
*
2
,
Transport
:
&
http
.
Transport
{
Proxy
:
http
.
ProxyFromEnvironment
},
}
func
(
this
*
thunderTask
)
fetch
()
error
{
this
.
Avatar
.
timestamp
=
time
.
Now
()
...
...
pkg/api/pluginproxy/ds_proxy.go
View file @
3c9798be
...
...
@@ -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
...
...
pkg/api/pluginproxy/ds_proxy_test.go
View file @
3c9798be
...
...
@@ -148,5 +148,18 @@ func TestDSRouteRule(t *testing.T) {
So
(
queryVals
[
"p"
][
0
],
ShouldEqual
,
"password"
)
})
})
Convey
(
"When interpolating string"
,
func
()
{
data
:=
templateData
{
SecureJsonData
:
map
[
string
]
string
{
"Test"
:
"0+0a0sdasd00+++"
,
},
}
interpolated
,
err
:=
interpolateString
(
"{{.SecureJsonData.Test}}"
,
data
)
So
(
err
,
ShouldBeNil
)
So
(
interpolated
,
ShouldEqual
,
"0+0a0sdasd00+++"
)
})
})
}
pkg/plugins/app_plugin.go
View file @
3c9798be
...
...
@@ -23,11 +23,12 @@ type AppPlugin struct {
}
type
AppPluginRoute
struct
{
Path
string
`json:"path"`
Method
string
`json:"method"`
ReqRole
models
.
RoleType
`json:"reqRole"`
Url
string
`json:"url"`
Headers
[]
AppPluginRouteHeader
`json:"headers"`
Path
string
`json:"path"`
Method
string
`json:"method"`
ReqRole
models
.
RoleType
`json:"reqRole"`
Url
string
`json:"url"`
Headers
[]
AppPluginRouteHeader
`json:"headers"`
TokenAuth
*
JwtTokenAuth
`json:"tokenAuth"`
}
type
AppPluginRouteHeader
struct
{
...
...
@@ -35,6 +36,11 @@ type AppPluginRouteHeader struct {
Content
string
`json:"content"`
}
type
JwtTokenAuth
struct
{
Url
string
`json:"url"`
Params
map
[
string
]
string
`json:"params"`
}
func
(
app
*
AppPlugin
)
Load
(
decoder
*
json
.
Decoder
,
pluginDir
string
)
error
{
if
err
:=
decoder
.
Decode
(
&
app
);
err
!=
nil
{
return
err
...
...
tests/datasource-test/module.js
View file @
3c9798be
...
...
@@ -8,12 +8,20 @@ System.register([], function (_export) {
function
Datasource
(
instanceSettings
,
backendSrv
)
{
this
.
url
=
instanceSettings
.
url
;
// this.testDatasource = function() {
// return backendSrv.datasourceRequest({
// method: 'GET',
// url: this.url + '/api/v4/search'
// });
// }
//
this
.
testDatasource
=
function
()
{
return
backendSrv
.
datasourceRequest
({
method
:
'GET'
,
url
:
this
.
url
+
'/
api/v4/search
'
url
:
this
.
url
+
'/
tokenTest
'
});
}
}
function
ConfigCtrl
()
{
...
...
@@ -22,12 +30,16 @@ System.register([], function (_export) {
ConfigCtrl
.
template
=
`
<div class="gf-form">
<label class="gf-form-label width-13">Email </label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.email'></input>
<label class="gf-form-label width-13">TenantId </label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.tenantId'></input>
</div>
<div class="gf-form">
<label class="gf-form-label width-13">ClientId </label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.jsonData.clientId'></input>
</div>
<div class="gf-form">
<label class="gf-form-label width-13">
Access key ID
</label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.secureJsonData.
token
'></input>
<label class="gf-form-label width-13">
Client secret
</label>
<input type="text" class="gf-form-input max-width-18" ng-model='ctrl.current.secureJsonData.
clientSecret
'></input>
</div>
`
;
...
...
tests/datasource-test/plugin.json
View file @
3c9798be
...
...
@@ -5,12 +5,12 @@
"routes"
:
[
{
"path"
:
"
api/v5/
"
,
"path"
:
"
tokenTest
"
,
"method"
:
"*"
,
"url"
:
"http
s://grafana-api.kentik.com/api/v5
"
,
"url"
:
"http
://localhost:3333/query
"
,
"tokenAuth"
:
{
"url"
:
"https://login.microsoftonline.com/{{.JsonData.tenantId}}/oauth2/token"
,
"
body
"
:
{
"url"
:
"https://login.microsoftonline.com/{{.JsonData.tenantId}}/oauth2/token"
,
"
params
"
:
{
"grant_type"
:
"client_credentials"
,
"client_id"
:
"{{.JsonData.clientId}}"
,
"client_secret"
:
"{{.SecureJsonData.clientSecret}}"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment