ds_proxy_test.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628
  1. package pluginproxy
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/grafana/grafana/pkg/components/securejsondata"
  6. "io/ioutil"
  7. "net/http"
  8. "net/url"
  9. "testing"
  10. "time"
  11. "golang.org/x/oauth2"
  12. macaron "gopkg.in/macaron.v1"
  13. "github.com/grafana/grafana/pkg/bus"
  14. "github.com/grafana/grafana/pkg/components/simplejson"
  15. "github.com/grafana/grafana/pkg/log"
  16. "github.com/grafana/grafana/pkg/login/social"
  17. m "github.com/grafana/grafana/pkg/models"
  18. "github.com/grafana/grafana/pkg/plugins"
  19. "github.com/grafana/grafana/pkg/setting"
  20. "github.com/grafana/grafana/pkg/util"
  21. . "github.com/smartystreets/goconvey/convey"
  22. )
  23. func TestDSRouteRule(t *testing.T) {
  24. Convey("DataSourceProxy", t, func() {
  25. Convey("Plugin with routes", func() {
  26. plugin := &plugins.DataSourcePlugin{
  27. Routes: []*plugins.AppPluginRoute{
  28. {
  29. Path: "api/v4/",
  30. Url: "https://www.google.com",
  31. ReqRole: m.ROLE_EDITOR,
  32. Headers: []plugins.AppPluginRouteHeader{
  33. {Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
  34. },
  35. },
  36. {
  37. Path: "api/admin",
  38. Url: "https://www.google.com",
  39. ReqRole: m.ROLE_ADMIN,
  40. Headers: []plugins.AppPluginRouteHeader{
  41. {Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
  42. },
  43. },
  44. {
  45. Path: "api/anon",
  46. Url: "https://www.google.com",
  47. Headers: []plugins.AppPluginRouteHeader{
  48. {Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
  49. },
  50. },
  51. {
  52. Path: "api/common",
  53. Url: "{{.JsonData.dynamicUrl}}",
  54. Headers: []plugins.AppPluginRouteHeader{
  55. {Name: "x-header", Content: "my secret {{.SecureJsonData.key}}"},
  56. },
  57. },
  58. },
  59. }
  60. setting.SecretKey = "password"
  61. key, _ := util.Encrypt([]byte("123"), "password")
  62. ds := &m.DataSource{
  63. JsonData: simplejson.NewFromAny(map[string]interface{}{
  64. "clientId": "asd",
  65. "dynamicUrl": "https://dynamic.grafana.com",
  66. }),
  67. SecureJsonData: map[string][]byte{
  68. "key": key,
  69. },
  70. }
  71. req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
  72. ctx := &m.ReqContext{
  73. Context: &macaron.Context{
  74. Req: macaron.Request{Request: req},
  75. },
  76. SignedInUser: &m.SignedInUser{OrgRole: m.ROLE_EDITOR},
  77. }
  78. Convey("When matching route path", func() {
  79. proxy := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method", &setting.Cfg{})
  80. proxy.route = plugin.Routes[0]
  81. ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
  82. Convey("should add headers and update url", func() {
  83. So(req.URL.String(), ShouldEqual, "https://www.google.com/some/method")
  84. So(req.Header.Get("x-header"), ShouldEqual, "my secret 123")
  85. })
  86. })
  87. Convey("When matching route path and has dynamic url", func() {
  88. proxy := NewDataSourceProxy(ds, plugin, ctx, "api/common/some/method", &setting.Cfg{})
  89. proxy.route = plugin.Routes[3]
  90. ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
  91. Convey("should add headers and interpolate the url", func() {
  92. So(req.URL.String(), ShouldEqual, "https://dynamic.grafana.com/some/method")
  93. So(req.Header.Get("x-header"), ShouldEqual, "my secret 123")
  94. })
  95. })
  96. Convey("Validating request", func() {
  97. Convey("plugin route with valid role", func() {
  98. proxy := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method", &setting.Cfg{})
  99. err := proxy.validateRequest()
  100. So(err, ShouldBeNil)
  101. })
  102. Convey("plugin route with admin role and user is editor", func() {
  103. proxy := NewDataSourceProxy(ds, plugin, ctx, "api/admin", &setting.Cfg{})
  104. err := proxy.validateRequest()
  105. So(err, ShouldNotBeNil)
  106. })
  107. Convey("plugin route with admin role and user is admin", func() {
  108. ctx.SignedInUser.OrgRole = m.ROLE_ADMIN
  109. proxy := NewDataSourceProxy(ds, plugin, ctx, "api/admin", &setting.Cfg{})
  110. err := proxy.validateRequest()
  111. So(err, ShouldBeNil)
  112. })
  113. })
  114. })
  115. Convey("Plugin with multiple routes for token auth", func() {
  116. plugin := &plugins.DataSourcePlugin{
  117. Routes: []*plugins.AppPluginRoute{
  118. {
  119. Path: "pathwithtoken1",
  120. Url: "https://api.nr1.io/some/path",
  121. TokenAuth: &plugins.JwtTokenAuth{
  122. Url: "https://login.server.com/{{.JsonData.tenantId}}/oauth2/token",
  123. Params: map[string]string{
  124. "grant_type": "client_credentials",
  125. "client_id": "{{.JsonData.clientId}}",
  126. "client_secret": "{{.SecureJsonData.clientSecret}}",
  127. "resource": "https://api.nr1.io",
  128. },
  129. },
  130. },
  131. {
  132. Path: "pathwithtoken2",
  133. Url: "https://api.nr2.io/some/path",
  134. TokenAuth: &plugins.JwtTokenAuth{
  135. Url: "https://login.server.com/{{.JsonData.tenantId}}/oauth2/token",
  136. Params: map[string]string{
  137. "grant_type": "client_credentials",
  138. "client_id": "{{.JsonData.clientId}}",
  139. "client_secret": "{{.SecureJsonData.clientSecret}}",
  140. "resource": "https://api.nr2.io",
  141. },
  142. },
  143. },
  144. },
  145. }
  146. setting.SecretKey = "password"
  147. key, _ := util.Encrypt([]byte("123"), "password")
  148. ds := &m.DataSource{
  149. JsonData: simplejson.NewFromAny(map[string]interface{}{
  150. "clientId": "asd",
  151. "tenantId": "mytenantId",
  152. }),
  153. SecureJsonData: map[string][]byte{
  154. "clientSecret": key,
  155. },
  156. }
  157. req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
  158. ctx := &m.ReqContext{
  159. Context: &macaron.Context{
  160. Req: macaron.Request{Request: req},
  161. },
  162. SignedInUser: &m.SignedInUser{OrgRole: m.ROLE_EDITOR},
  163. }
  164. Convey("When creating and caching access tokens", func() {
  165. var authorizationHeaderCall1 string
  166. var authorizationHeaderCall2 string
  167. Convey("first call should add authorization header with access token", func() {
  168. json, err := ioutil.ReadFile("./test-data/access-token-1.json")
  169. So(err, ShouldBeNil)
  170. client = newFakeHTTPClient(json)
  171. proxy1 := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
  172. proxy1.route = plugin.Routes[0]
  173. ApplyRoute(proxy1.ctx.Req.Context(), req, proxy1.proxyPath, proxy1.route, proxy1.ds)
  174. authorizationHeaderCall1 = req.Header.Get("Authorization")
  175. So(req.URL.String(), ShouldEqual, "https://api.nr1.io/some/path")
  176. So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
  177. Convey("second call to another route should add a different access token", func() {
  178. json2, err := ioutil.ReadFile("./test-data/access-token-2.json")
  179. So(err, ShouldBeNil)
  180. req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
  181. client = newFakeHTTPClient(json2)
  182. proxy2 := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken2", &setting.Cfg{})
  183. proxy2.route = plugin.Routes[1]
  184. ApplyRoute(proxy2.ctx.Req.Context(), req, proxy2.proxyPath, proxy2.route, proxy2.ds)
  185. authorizationHeaderCall2 = req.Header.Get("Authorization")
  186. So(req.URL.String(), ShouldEqual, "https://api.nr2.io/some/path")
  187. So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
  188. So(authorizationHeaderCall2, ShouldStartWith, "Bearer eyJ0e")
  189. So(authorizationHeaderCall2, ShouldNotEqual, authorizationHeaderCall1)
  190. Convey("third call to first route should add cached access token", func() {
  191. req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
  192. client = newFakeHTTPClient([]byte{})
  193. proxy3 := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
  194. proxy3.route = plugin.Routes[0]
  195. ApplyRoute(proxy3.ctx.Req.Context(), req, proxy3.proxyPath, proxy3.route, proxy3.ds)
  196. authorizationHeaderCall3 := req.Header.Get("Authorization")
  197. So(req.URL.String(), ShouldEqual, "https://api.nr1.io/some/path")
  198. So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
  199. So(authorizationHeaderCall3, ShouldStartWith, "Bearer eyJ0e")
  200. So(authorizationHeaderCall3, ShouldEqual, authorizationHeaderCall1)
  201. })
  202. })
  203. })
  204. })
  205. })
  206. Convey("When proxying graphite", func() {
  207. setting.BuildVersion = "5.3.0"
  208. plugin := &plugins.DataSourcePlugin{}
  209. ds := &m.DataSource{Url: "htttp://graphite:8080", Type: m.DS_GRAPHITE}
  210. ctx := &m.ReqContext{}
  211. proxy := NewDataSourceProxy(ds, plugin, ctx, "/render", &setting.Cfg{})
  212. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  213. So(err, ShouldBeNil)
  214. proxy.getDirector()(req)
  215. Convey("Can translate request url and path", func() {
  216. So(req.URL.Host, ShouldEqual, "graphite:8080")
  217. So(req.URL.Path, ShouldEqual, "/render")
  218. So(req.Header.Get("User-Agent"), ShouldEqual, "Grafana/5.3.0")
  219. })
  220. })
  221. Convey("When proxying InfluxDB", func() {
  222. plugin := &plugins.DataSourcePlugin{}
  223. ds := &m.DataSource{
  224. Type: m.DS_INFLUXDB_08,
  225. Url: "http://influxdb:8083",
  226. Database: "site",
  227. User: "user",
  228. Password: "password",
  229. }
  230. ctx := &m.ReqContext{}
  231. proxy := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
  232. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  233. So(err, ShouldBeNil)
  234. proxy.getDirector()(req)
  235. Convey("Should add db to url", func() {
  236. So(req.URL.Path, ShouldEqual, "/db/site/")
  237. })
  238. })
  239. Convey("When proxying a data source with no keepCookies specified", func() {
  240. plugin := &plugins.DataSourcePlugin{}
  241. json, _ := simplejson.NewJson([]byte(`{"keepCookies": []}`))
  242. ds := &m.DataSource{
  243. Type: m.DS_GRAPHITE,
  244. Url: "http://graphite:8086",
  245. JsonData: json,
  246. }
  247. ctx := &m.ReqContext{}
  248. proxy := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
  249. requestURL, _ := url.Parse("http://grafana.com/sub")
  250. req := http.Request{URL: requestURL, Header: make(http.Header)}
  251. cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
  252. req.Header.Set("Cookie", cookies)
  253. proxy.getDirector()(&req)
  254. Convey("Should clear all cookies", func() {
  255. So(req.Header.Get("Cookie"), ShouldEqual, "")
  256. })
  257. })
  258. Convey("When proxying a data source with keep cookies specified", func() {
  259. plugin := &plugins.DataSourcePlugin{}
  260. json, _ := simplejson.NewJson([]byte(`{"keepCookies": ["JSESSION_ID"]}`))
  261. ds := &m.DataSource{
  262. Type: m.DS_GRAPHITE,
  263. Url: "http://graphite:8086",
  264. JsonData: json,
  265. }
  266. ctx := &m.ReqContext{}
  267. proxy := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
  268. requestURL, _ := url.Parse("http://grafana.com/sub")
  269. req := http.Request{URL: requestURL, Header: make(http.Header)}
  270. cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
  271. req.Header.Set("Cookie", cookies)
  272. proxy.getDirector()(&req)
  273. Convey("Should keep named cookies", func() {
  274. So(req.Header.Get("Cookie"), ShouldEqual, "JSESSION_ID=test")
  275. })
  276. })
  277. Convey("When proxying a data source with custom headers specified", func() {
  278. plugin := &plugins.DataSourcePlugin{}
  279. encryptedData, err := util.Encrypt([]byte(`Bearer xf5yhfkpsnmgo`), setting.SecretKey)
  280. ds := &m.DataSource{
  281. Type: m.DS_PROMETHEUS,
  282. Url: "http://prometheus:9090",
  283. JsonData: simplejson.NewFromAny(map[string]interface{}{
  284. "httpHeaderName1": "Authorization",
  285. }),
  286. SecureJsonData: map[string][]byte{
  287. "httpHeaderValue1": encryptedData,
  288. },
  289. }
  290. ctx := &m.ReqContext{}
  291. proxy := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
  292. requestURL, _ := url.Parse("http://grafana.com/sub")
  293. req := http.Request{URL: requestURL, Header: make(http.Header)}
  294. proxy.getDirector()(&req)
  295. if err != nil {
  296. log.Fatal(4, err.Error())
  297. }
  298. Convey("Match header value after decryption", func() {
  299. So(req.Header.Get("Authorization"), ShouldEqual, "Bearer xf5yhfkpsnmgo")
  300. })
  301. })
  302. Convey("When proxying a custom datasource", func() {
  303. plugin := &plugins.DataSourcePlugin{}
  304. ds := &m.DataSource{
  305. Type: "custom-datasource",
  306. Url: "http://host/root/",
  307. }
  308. ctx := &m.ReqContext{}
  309. proxy := NewDataSourceProxy(ds, plugin, ctx, "/path/to/folder/", &setting.Cfg{})
  310. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  311. req.Header.Add("Origin", "grafana.com")
  312. req.Header.Add("Referer", "grafana.com")
  313. req.Header.Add("X-Canary", "stillthere")
  314. So(err, ShouldBeNil)
  315. proxy.getDirector()(req)
  316. Convey("Should keep user request (including trailing slash)", func() {
  317. So(req.URL.String(), ShouldEqual, "http://host/root/path/to/folder/")
  318. })
  319. Convey("Origin and Referer headers should be dropped", func() {
  320. So(req.Header.Get("Origin"), ShouldEqual, "")
  321. So(req.Header.Get("Referer"), ShouldEqual, "")
  322. So(req.Header.Get("X-Canary"), ShouldEqual, "stillthere")
  323. })
  324. })
  325. Convey("When proxying a datasource that has oauth token pass-thru enabled", func() {
  326. social.SocialMap["generic_oauth"] = &social.SocialGenericOAuth{
  327. SocialBase: &social.SocialBase{
  328. Config: &oauth2.Config{},
  329. },
  330. }
  331. bus.AddHandler("test", func(query *m.GetAuthInfoQuery) error {
  332. query.Result = &m.UserAuth{
  333. Id: 1,
  334. UserId: 1,
  335. AuthModule: "generic_oauth",
  336. OAuthAccessToken: "testtoken",
  337. OAuthRefreshToken: "testrefreshtoken",
  338. OAuthTokenType: "Bearer",
  339. OAuthExpiry: time.Now().AddDate(0, 0, 1),
  340. }
  341. return nil
  342. })
  343. plugin := &plugins.DataSourcePlugin{}
  344. ds := &m.DataSource{
  345. Type: "custom-datasource",
  346. Url: "http://host/root/",
  347. JsonData: simplejson.NewFromAny(map[string]interface{}{
  348. "oauthPassThru": true,
  349. }),
  350. }
  351. req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
  352. ctx := &m.ReqContext{
  353. SignedInUser: &m.SignedInUser{UserId: 1},
  354. Context: &macaron.Context{
  355. Req: macaron.Request{Request: req},
  356. },
  357. }
  358. proxy := NewDataSourceProxy(ds, plugin, ctx, "/path/to/folder/", &setting.Cfg{})
  359. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  360. So(err, ShouldBeNil)
  361. proxy.getDirector()(req)
  362. Convey("Should have access token in header", func() {
  363. So(req.Header.Get("Authorization"), ShouldEqual, fmt.Sprintf("%s %s", "Bearer", "testtoken"))
  364. })
  365. })
  366. Convey("When SendUserHeader config is enabled", func() {
  367. req := getDatasourceProxiedRequest(
  368. &m.ReqContext{
  369. SignedInUser: &m.SignedInUser{
  370. Login: "test_user",
  371. },
  372. },
  373. &setting.Cfg{SendUserHeader: true},
  374. )
  375. Convey("Should add header with username", func() {
  376. So(req.Header.Get("X-Grafana-User"), ShouldEqual, "test_user")
  377. })
  378. })
  379. Convey("When SendUserHeader config is disabled", func() {
  380. req := getDatasourceProxiedRequest(
  381. &m.ReqContext{
  382. SignedInUser: &m.SignedInUser{
  383. Login: "test_user",
  384. },
  385. },
  386. &setting.Cfg{SendUserHeader: false},
  387. )
  388. Convey("Should not add header with username", func() {
  389. // Get will return empty string even if header is not set
  390. So(req.Header.Get("X-Grafana-User"), ShouldEqual, "")
  391. })
  392. })
  393. Convey("When SendUserHeader config is enabled but user is anonymous", func() {
  394. req := getDatasourceProxiedRequest(
  395. &m.ReqContext{
  396. SignedInUser: &m.SignedInUser{IsAnonymous: true},
  397. },
  398. &setting.Cfg{SendUserHeader: true},
  399. )
  400. Convey("Should not add header with username", func() {
  401. // Get will return empty string even if header is not set
  402. So(req.Header.Get("X-Grafana-User"), ShouldEqual, "")
  403. })
  404. })
  405. Convey("When proxying data source proxy should handle authentication", func() {
  406. tests := []*Test{
  407. createAuthTest(m.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, false),
  408. createAuthTest(m.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, true),
  409. createAuthTest(m.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, true),
  410. createAuthTest(m.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, false),
  411. createAuthTest(m.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
  412. createAuthTest(m.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
  413. // These two should be enough for any other datasource at the moment. Proxy has special handling
  414. // only for Influx, others have the same path and only BasicAuth. Non BasicAuth datasources
  415. // do not go through proxy but through TSDB API which is not tested here.
  416. createAuthTest(m.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
  417. createAuthTest(m.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
  418. }
  419. for _, test := range tests {
  420. runDatasourceAuthTest(test)
  421. }
  422. })
  423. })
  424. }
  425. // getDatasourceProxiedRequest is a helper for easier setup of tests based on global config and ReqContext.
  426. func getDatasourceProxiedRequest(ctx *m.ReqContext, cfg *setting.Cfg) *http.Request {
  427. plugin := &plugins.DataSourcePlugin{}
  428. ds := &m.DataSource{
  429. Type: "custom",
  430. Url: "http://host/root/",
  431. }
  432. proxy := NewDataSourceProxy(ds, plugin, ctx, "", cfg)
  433. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  434. So(err, ShouldBeNil)
  435. proxy.getDirector()(req)
  436. return req
  437. }
  438. type httpClientStub struct {
  439. fakeBody []byte
  440. }
  441. func (c *httpClientStub) Do(req *http.Request) (*http.Response, error) {
  442. bodyJSON, _ := simplejson.NewJson(c.fakeBody)
  443. _, passedTokenCacheTest := bodyJSON.CheckGet("expires_on")
  444. So(passedTokenCacheTest, ShouldBeTrue)
  445. bodyJSON.Set("expires_on", fmt.Sprint(time.Now().Add(time.Second*60).Unix()))
  446. body, _ := bodyJSON.MarshalJSON()
  447. resp := &http.Response{
  448. Body: ioutil.NopCloser(bytes.NewReader(body)),
  449. }
  450. return resp, nil
  451. }
  452. func newFakeHTTPClient(fakeBody []byte) httpClient {
  453. return &httpClientStub{
  454. fakeBody: fakeBody,
  455. }
  456. }
  457. type Test struct {
  458. datasource *m.DataSource
  459. checkReq func(req *http.Request)
  460. }
  461. const (
  462. AUTHTYPE_PASSWORD = "password"
  463. AUTHTYPE_BASIC = "basic"
  464. )
  465. const (
  466. AUTHCHECK_QUERY = "query"
  467. AUTHCHECK_HEADER = "header"
  468. )
  469. func createAuthTest(dsType string, authType string, authCheck string, useSecureJsonData bool) *Test {
  470. // Basic user:password
  471. base64AthHeader := "Basic dXNlcjpwYXNzd29yZA=="
  472. test := &Test{
  473. datasource: &m.DataSource{
  474. Type: dsType,
  475. JsonData: simplejson.New(),
  476. },
  477. }
  478. var message string
  479. if authType == AUTHTYPE_PASSWORD {
  480. message = fmt.Sprintf("%v should add username and password", dsType)
  481. test.datasource.User = "user"
  482. if useSecureJsonData {
  483. test.datasource.SecureJsonData = securejsondata.GetEncryptedJsonData(map[string]string{
  484. "password": "password",
  485. })
  486. } else {
  487. test.datasource.Password = "password"
  488. }
  489. } else {
  490. message = fmt.Sprintf("%v should add basic auth username and password", dsType)
  491. test.datasource.BasicAuth = true
  492. test.datasource.BasicAuthUser = "user"
  493. if useSecureJsonData {
  494. test.datasource.SecureJsonData = securejsondata.GetEncryptedJsonData(map[string]string{
  495. "basicAuthPassword": "password",
  496. })
  497. } else {
  498. test.datasource.BasicAuthPassword = "password"
  499. }
  500. }
  501. if useSecureJsonData {
  502. message += " from securejsondata"
  503. }
  504. if authCheck == AUTHCHECK_QUERY {
  505. message += " to query params"
  506. test.checkReq = func(req *http.Request) {
  507. Convey(message, func() {
  508. queryVals := req.URL.Query()
  509. So(queryVals["u"][0], ShouldEqual, "user")
  510. So(queryVals["p"][0], ShouldEqual, "password")
  511. })
  512. }
  513. } else {
  514. message += " to auth header"
  515. test.checkReq = func(req *http.Request) {
  516. Convey(message, func() {
  517. So(req.Header.Get("Authorization"), ShouldEqual, base64AthHeader)
  518. })
  519. }
  520. }
  521. return test
  522. }
  523. func runDatasourceAuthTest(test *Test) {
  524. plugin := &plugins.DataSourcePlugin{}
  525. ctx := &m.ReqContext{}
  526. proxy := NewDataSourceProxy(test.datasource, plugin, ctx, "", &setting.Cfg{})
  527. req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
  528. So(err, ShouldBeNil)
  529. proxy.getDirector()(req)
  530. test.checkReq(req)
  531. }