2017-08-22 10:14:15 -05:00
package pluginproxy
import (
2018-06-12 10:39:38 -05:00
"bytes"
2021-07-07 01:54:17 -05:00
"context"
2022-04-25 11:57:45 -05:00
"encoding/json"
2022-12-01 13:51:12 -06:00
"errors"
2018-06-12 10:39:38 -05:00
"fmt"
2022-08-10 08:37:51 -05:00
"io"
2017-08-22 10:14:15 -05:00
"net/http"
2019-05-01 09:32:03 -05:00
"net/http/httptest"
2017-08-23 03:52:31 -05:00
"net/url"
2022-08-10 08:37:51 -05:00
"os"
2020-04-22 03:30:06 -05:00
"strings"
2017-08-22 10:14:15 -05:00
"testing"
2018-06-12 10:39:38 -05:00
"time"
2017-08-22 10:14:15 -05:00
2022-06-27 11:23:15 -05:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
2020-06-17 04:17:11 -05:00
"github.com/grafana/grafana/pkg/api/datasource"
2021-05-19 16:53:41 -05:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
2021-05-19 16:53:41 -05:00
"github.com/grafana/grafana/pkg/infra/httpclient"
2022-08-25 16:04:44 -05:00
"github.com/grafana/grafana/pkg/infra/log"
2022-01-20 04:10:12 -06:00
"github.com/grafana/grafana/pkg/infra/tracing"
2020-03-04 05:57:20 -06:00
"github.com/grafana/grafana/pkg/models"
2021-03-08 00:02:49 -06:00
"github.com/grafana/grafana/pkg/plugins"
2022-01-18 10:34:35 -06:00
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2022-06-27 11:23:15 -05:00
"github.com/grafana/grafana/pkg/services/datasources"
2022-02-11 08:52:14 -06:00
datasourceservice "github.com/grafana/grafana/pkg/services/datasources/service"
2022-02-17 07:03:45 -06:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2021-07-07 01:54:17 -05:00
"github.com/grafana/grafana/pkg/services/oauthtoken"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/org"
2022-11-14 13:08:10 -06:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2021-11-04 11:47:21 -05:00
"github.com/grafana/grafana/pkg/services/secrets"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
2022-08-25 16:04:44 -05:00
secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
secretsmng "github.com/grafana/grafana/pkg/services/secrets/manager"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/user"
2021-05-19 16:53:41 -05:00
"github.com/grafana/grafana/pkg/setting"
2021-10-11 07:30:59 -05:00
"github.com/grafana/grafana/pkg/web"
2017-08-22 10:14:15 -05:00
)
2020-11-13 02:52:38 -06:00
func TestDataSourceProxy_routeRule ( t * testing . T ) {
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2021-05-19 16:53:41 -05:00
httpClientProvider := httpclient . NewProvider ( )
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2021-05-19 16:53:41 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "Plugin with routes" , func ( t * testing . T ) {
2021-11-01 04:53:33 -05:00
routes := [ ] * plugins . Route {
{
Path : "api/v4/" ,
URL : "https://www.google.com" ,
2022-08-10 04:56:48 -05:00
ReqRole : org . RoleEditor ,
2021-11-01 04:53:33 -05:00
Headers : [ ] plugins . Header {
{ Name : "x-header" , Content : "my secret {{.SecureJsonData.key}}" } ,
2020-11-13 02:52:38 -06:00
} ,
2021-11-01 04:53:33 -05:00
} ,
{
Path : "api/admin" ,
URL : "https://www.google.com" ,
2022-08-10 04:56:48 -05:00
ReqRole : org . RoleAdmin ,
2021-11-01 04:53:33 -05:00
Headers : [ ] plugins . Header {
{ Name : "x-header" , Content : "my secret {{.SecureJsonData.key}}" } ,
2020-11-13 02:52:38 -06:00
} ,
2021-11-01 04:53:33 -05:00
} ,
{
Path : "api/anon" ,
URL : "https://www.google.com" ,
Headers : [ ] plugins . Header {
{ Name : "x-header" , Content : "my secret {{.SecureJsonData.key}}" } ,
2017-08-22 10:14:15 -05:00
} ,
2021-11-01 04:53:33 -05:00
} ,
{
Path : "api/common" ,
URL : "{{.JsonData.dynamicUrl}}" ,
URLParams : [ ] plugins . URLParam {
{ Name : "{{.JsonData.queryParam}}" , Content : "{{.SecureJsonData.key}}" } ,
2017-08-23 03:52:31 -05:00
} ,
2021-11-01 04:53:33 -05:00
Headers : [ ] plugins . Header {
{ Name : "x-header" , Content : "my secret {{.SecureJsonData.key}}" } ,
2021-03-31 09:38:35 -05:00
} ,
2020-11-13 02:52:38 -06:00
} ,
2021-11-01 04:53:33 -05:00
{
Path : "api/restricted" ,
2022-08-10 04:56:48 -05:00
ReqRole : org . RoleAdmin ,
2021-11-01 04:53:33 -05:00
} ,
{
Path : "api/body" ,
URL : "http://www.test.com" ,
Body : [ ] byte ( ` { "url": " {{ .JsonData .dynamicUrl }} ", "secret": " {{ .SecureJsonData .key }} " } ` ) ,
} ,
2020-11-13 02:52:38 -06:00
}
2017-08-22 10:14:15 -05:00
2020-11-13 02:52:38 -06:00
origSecretKey := setting . SecretKey
t . Cleanup ( func ( ) {
setting . SecretKey = origSecretKey
} )
setting . SecretKey = "password" //nolint:goconst
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2021-11-04 11:47:21 -05:00
key , err := secretsService . Encrypt ( context . Background ( ) , [ ] byte ( "123" ) , secrets . WithoutScope ( ) )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
2020-11-13 02:52:38 -06:00
JsonData : simplejson . NewFromAny ( map [ string ] interface { } {
"clientId" : "asd" ,
"dynamicUrl" : "https://dynamic.grafana.com" ,
"queryParam" : "apiKey" ,
} ) ,
SecureJsonData : map [ string ] [ ] byte {
"key" : key ,
} ,
}
2021-10-08 07:46:35 -05:00
jd , err := ds . JsonData . Map ( )
require . NoError ( t , err )
dsInfo := DSInfo {
ID : ds . Id ,
Updated : ds . Updated ,
JSONData : jd ,
DecryptedSecureJSONData : map [ string ] string {
"key" : "123" ,
} ,
}
2020-11-13 02:52:38 -06:00
setUp := func ( ) ( * models . ReqContext , * http . Request ) {
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
2020-03-04 05:57:20 -06:00
ctx := & models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { Req : req } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleEditor } ,
2017-08-23 03:52:31 -05:00
}
2020-11-13 02:52:38 -06:00
return ctx , req
}
2017-08-22 10:14:15 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When matching route path" , func ( t * testing . T ) {
ctx , req := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/v4/some/method" , cfg , httpClientProvider ,
2022-04-25 11:57:45 -05:00
& oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
proxy . matchedRoute = routes [ 0 ]
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , proxy . matchedRoute , dsInfo , cfg )
2017-08-22 10:14:15 -05:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "https://www.google.com/some/method" , req . URL . String ( ) )
assert . Equal ( t , "my secret 123" , req . Header . Get ( "x-header" ) )
} )
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When matching route path and has dynamic url" , func ( t * testing . T ) {
ctx , req := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/common/some/method" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
proxy . matchedRoute = routes [ 3 ]
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , proxy . matchedRoute , dsInfo , cfg )
2018-08-18 09:00:40 -05:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "https://dynamic.grafana.com/some/method?apiKey=123" , req . URL . String ( ) )
assert . Equal ( t , "my secret 123" , req . Header . Get ( "x-header" ) )
} )
2018-08-18 09:00:40 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When matching route path with no url" , func ( t * testing . T ) {
ctx , req := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
proxy . matchedRoute = routes [ 4 ]
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , proxy . matchedRoute , dsInfo , cfg )
2020-09-18 06:22:07 -05:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "http://localhost/asd" , req . URL . String ( ) )
} )
2020-09-18 06:22:07 -05:00
2021-03-31 09:38:35 -05:00
t . Run ( "When matching route path and has dynamic body" , func ( t * testing . T ) {
ctx , req := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/body" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2021-03-31 09:38:35 -05:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
proxy . matchedRoute = routes [ 5 ]
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , proxy . matchedRoute , dsInfo , cfg )
2021-03-31 09:38:35 -05:00
2022-08-10 08:37:51 -05:00
content , err := io . ReadAll ( req . Body )
2021-03-31 09:38:35 -05:00
require . NoError ( t , err )
require . Equal ( t , ` { "url": "https://dynamic.grafana.com", "secret": "123" } ` , string ( content ) )
} )
2020-11-13 02:52:38 -06:00
t . Run ( "Validating request" , func ( t * testing . T ) {
t . Run ( "plugin route with valid role" , func ( t * testing . T ) {
ctx , _ := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/v4/some/method" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
err = proxy . validateRequest ( )
require . NoError ( t , err )
} )
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "plugin route with admin role and user is editor" , func ( t * testing . T ) {
ctx , _ := setUp ( )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/admin" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
err = proxy . validateRequest ( )
require . Error ( t , err )
} )
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "plugin route with admin role and user is admin" , func ( t * testing . T ) {
ctx , _ := setUp ( )
2022-08-10 04:56:48 -05:00
ctx . SignedInUser . OrgRole = org . RoleAdmin
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "api/admin" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
err = proxy . validateRequest ( )
require . NoError ( t , err )
2017-08-22 10:14:15 -05:00
} )
} )
2020-11-13 02:52:38 -06:00
} )
2017-08-22 10:14:15 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "Plugin with multiple routes for token auth" , func ( t * testing . T ) {
2021-11-01 04:53:33 -05:00
routes := [ ] * plugins . Route {
{
Path : "pathwithtoken1" ,
URL : "https://api.nr1.io/some/path" ,
TokenAuth : & plugins . JWTTokenAuth {
Url : "https://login.server.com/{{.JsonData.tenantId}}/oauth2/token" ,
Params : map [ string ] string {
"grant_type" : "client_credentials" ,
"client_id" : "{{.JsonData.clientId}}" ,
"client_secret" : "{{.SecureJsonData.clientSecret}}" ,
"resource" : "https://api.nr1.io" ,
2018-06-12 10:39:38 -05:00
} ,
2020-11-13 02:52:38 -06:00
} ,
2021-11-01 04:53:33 -05:00
} ,
{
Path : "pathwithtoken2" ,
URL : "https://api.nr2.io/some/path" ,
TokenAuth : & plugins . JWTTokenAuth {
Url : "https://login.server.com/{{.JsonData.tenantId}}/oauth2/token" ,
Params : map [ string ] string {
"grant_type" : "client_credentials" ,
"client_id" : "{{.JsonData.clientId}}" ,
"client_secret" : "{{.SecureJsonData.clientSecret}}" ,
"resource" : "https://api.nr2.io" ,
2018-06-12 10:39:38 -05:00
} ,
} ,
2020-11-13 02:52:38 -06:00
} ,
}
2018-06-12 10:39:38 -05:00
2020-11-13 02:52:38 -06:00
origSecretKey := setting . SecretKey
t . Cleanup ( func ( ) {
setting . SecretKey = origSecretKey
} )
setting . SecretKey = "password"
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2021-11-04 11:47:21 -05:00
key , err := secretsService . Encrypt ( context . Background ( ) , [ ] byte ( "123" ) , secrets . WithoutScope ( ) )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
2020-11-13 02:52:38 -06:00
JsonData : simplejson . NewFromAny ( map [ string ] interface { } {
"clientId" : "asd" ,
"tenantId" : "mytenantId" ,
} ) ,
SecureJsonData : map [ string ] [ ] byte {
"clientSecret" : key ,
} ,
}
2018-06-12 10:39:38 -05:00
2020-11-13 02:52:38 -06:00
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
ctx := & models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { Req : req } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleEditor } ,
2020-11-13 02:52:38 -06:00
}
2018-06-12 10:39:38 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When creating and caching access tokens" , func ( t * testing . T ) {
var authorizationHeaderCall1 string
var authorizationHeaderCall2 string
2018-06-12 10:39:38 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "first call should add authorization header with access token" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
json , err := os . ReadFile ( "./test-data/access-token-1.json" )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-05-03 07:46:32 -05:00
originalClient := client
2020-11-13 02:52:38 -06:00
client = newFakeHTTPClient ( t , json )
2021-05-03 07:46:32 -05:00
defer func ( ) { client = originalClient } ( )
2021-10-08 07:46:35 -05:00
jd , err := ds . JsonData . Map ( )
require . NoError ( t , err )
dsInfo := DSInfo {
ID : ds . Id ,
Updated : ds . Updated ,
JSONData : jd ,
DecryptedSecureJSONData : map [ string ] string {
"clientSecret" : "123" ,
} ,
}
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "pathwithtoken1" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , routes [ 0 ] , dsInfo , cfg )
2020-11-13 02:52:38 -06:00
authorizationHeaderCall1 = req . Header . Get ( "Authorization" )
assert . Equal ( t , "https://api.nr1.io/some/path" , req . URL . String ( ) )
assert . True ( t , strings . HasPrefix ( authorizationHeaderCall1 , "Bearer eyJ0e" ) )
t . Run ( "second call to another route should add a different access token" , func ( t * testing . T ) {
2022-08-10 08:37:51 -05:00
json2 , err := os . ReadFile ( "./test-data/access-token-2.json" )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
client = newFakeHTTPClient ( t , json2 )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "pathwithtoken2" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , routes [ 1 ] , dsInfo , cfg )
2020-11-13 02:52:38 -06:00
authorizationHeaderCall2 = req . Header . Get ( "Authorization" )
assert . Equal ( t , "https://api.nr2.io/some/path" , req . URL . String ( ) )
assert . True ( t , strings . HasPrefix ( authorizationHeaderCall1 , "Bearer eyJ0e" ) )
assert . True ( t , strings . HasPrefix ( authorizationHeaderCall2 , "Bearer eyJ0e" ) )
assert . NotEqual ( t , authorizationHeaderCall1 , authorizationHeaderCall2 )
t . Run ( "third call to first route should add cached access token" , func ( t * testing . T ) {
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
client = newFakeHTTPClient ( t , [ ] byte { } )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "pathwithtoken1" , cfg , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2021-11-01 04:53:33 -05:00
ApplyRoute ( proxy . ctx . Req . Context ( ) , req , proxy . proxyPath , routes [ 0 ] , dsInfo , cfg )
2020-11-13 02:52:38 -06:00
authorizationHeaderCall3 := req . Header . Get ( "Authorization" )
assert . Equal ( t , "https://api.nr1.io/some/path" , req . URL . String ( ) )
assert . True ( t , strings . HasPrefix ( authorizationHeaderCall1 , "Bearer eyJ0e" ) )
assert . True ( t , strings . HasPrefix ( authorizationHeaderCall3 , "Bearer eyJ0e" ) )
assert . Equal ( t , authorizationHeaderCall1 , authorizationHeaderCall3 )
2018-06-12 10:39:38 -05:00
} )
} )
} )
} )
2020-11-13 02:52:38 -06:00
} )
2018-06-12 10:39:38 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying graphite" , func ( t * testing . T ) {
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource { Url : "htttp://graphite:8080" , Type : datasources . DS_GRAPHITE }
2020-11-13 02:52:38 -06:00
ctx := & models . ReqContext { }
2017-08-23 03:52:31 -05:00
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/render" , & setting . Cfg { BuildVersion : "5.3.0" } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
req , err := http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
require . NoError ( t , err )
2017-08-23 03:52:31 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2017-08-22 10:14:15 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "Can translate request URL and path" , func ( t * testing . T ) {
assert . Equal ( t , "graphite:8080" , req . URL . Host )
assert . Equal ( t , "/render" , req . URL . Path )
} )
} )
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying InfluxDB" , func ( t * testing . T ) {
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
Type : datasources . DS_INFLUXDB_08 ,
2020-11-13 02:52:38 -06:00
Url : "http://influxdb:8083" ,
Database : "site" ,
User : "user" ,
}
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
ctx := & models . ReqContext { }
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2017-08-23 03:52:31 -05:00
2020-11-13 02:52:38 -06:00
req , err := http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
require . NoError ( t , err )
2017-08-22 10:14:15 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "/db/site/" , req . URL . Path )
} )
2017-08-23 10:18:43 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying a data source with no keepCookies specified" , func ( t * testing . T ) {
json , err := simplejson . NewJson ( [ ] byte ( ` { "keepCookies": []} ` ) )
require . NoError ( t , err )
2017-10-17 13:48:01 -05:00
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
Type : datasources . DS_GRAPHITE ,
2020-11-13 02:52:38 -06:00
Url : "http://graphite:8086" ,
JsonData : json ,
}
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
ctx := & models . ReqContext { }
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
requestURL , err := url . Parse ( "http://grafana.com/sub" )
require . NoError ( t , err )
req := http . Request { URL : requestURL , Header : make ( http . Header ) }
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
req . Header . Set ( "Cookie" , cookies )
2017-10-17 13:48:01 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( & req )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "" , req . Header . Get ( "Cookie" ) )
} )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying a data source with keep cookies specified" , func ( t * testing . T ) {
json , err := simplejson . NewJson ( [ ] byte ( ` { "keepCookies": ["JSESSION_ID"]} ` ) )
require . NoError ( t , err )
2017-10-17 13:48:01 -05:00
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
Type : datasources . DS_GRAPHITE ,
2020-11-13 02:52:38 -06:00
Url : "http://graphite:8086" ,
JsonData : json ,
}
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
ctx := & models . ReqContext { }
2021-11-01 04:53:33 -05:00
var pluginRoutes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , pluginRoutes , ctx , "" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
requestURL , err := url . Parse ( "http://grafana.com/sub" )
require . NoError ( t , err )
req := http . Request { URL : requestURL , Header : make ( http . Header ) }
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
req . Header . Set ( "Cookie" , cookies )
2017-10-17 13:48:01 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( & req )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "JSESSION_ID=test" , req . Header . Get ( "Cookie" ) )
} )
2017-10-17 13:48:01 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying a custom datasource" , func ( t * testing . T ) {
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
2020-11-13 02:52:38 -06:00
Type : "custom-datasource" ,
Url : "http://host/root/" ,
}
ctx := & models . ReqContext { }
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/path/to/folder/" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
req , err := http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
2020-12-14 08:13:01 -06:00
req . Header . Set ( "Origin" , "grafana.com" )
req . Header . Set ( "Referer" , "grafana.com" )
req . Header . Set ( "X-Canary" , "stillthere" )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "http://host/root/path/to/folder/" , req . URL . String ( ) )
assert . Equal ( t , "stillthere" , req . Header . Get ( "X-Canary" ) )
} )
2018-11-08 05:30:10 -06:00
2020-11-13 06:21:43 -06:00
t . Run ( "When proxying a datasource that has OAuth token pass-through enabled" , func ( t * testing . T ) {
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
2020-11-13 02:52:38 -06:00
Type : "custom-datasource" ,
Url : "http://host/root/" ,
JsonData : simplejson . NewFromAny ( map [ string ] interface { } {
"oauthPassThru" : true ,
} ) ,
}
2019-02-01 18:40:57 -06:00
2020-11-13 02:52:38 -06:00
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
ctx := & models . ReqContext {
2022-08-11 06:28:55 -05:00
SignedInUser : & user . SignedInUser { UserID : 1 } ,
2021-10-11 07:30:59 -05:00
Context : & web . Context { Req : req } ,
2020-11-13 02:52:38 -06:00
}
2021-11-29 08:40:05 -06:00
token := & oauth2 . Token {
AccessToken : "testtoken" ,
RefreshToken : "testrefreshtoken" ,
TokenType : "Bearer" ,
Expiry : time . Now ( ) . AddDate ( 0 , 0 , 1 ) ,
}
extra := map [ string ] interface { } {
"id_token" : "testidtoken" ,
}
token = token . WithExtra ( extra )
2021-07-07 01:54:17 -05:00
mockAuthToken := mockOAuthTokenService {
2021-11-29 08:40:05 -06:00
token : token ,
2021-07-07 01:54:17 -05:00
oAuthEnabled : true ,
}
2021-11-29 08:40:05 -06:00
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/path/to/folder/" , & setting . Cfg { } , httpClientProvider , & mockAuthToken , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
req , err = http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
require . NoError ( t , err )
2019-02-01 18:40:57 -06:00
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2019-02-01 18:40:57 -06:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , "Bearer testtoken" , req . Header . Get ( "Authorization" ) )
2021-11-29 08:40:05 -06:00
assert . Equal ( t , "testidtoken" , req . Header . Get ( "X-ID-Token" ) )
2020-11-13 02:52:38 -06:00
} )
2019-03-14 12:18:00 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When SendUserHeader config is enabled" , func ( t * testing . T ) {
req := getDatasourceProxiedRequest (
t ,
& models . ReqContext {
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser {
2020-11-13 02:52:38 -06:00
Login : "test_user" ,
2019-03-14 07:04:47 -05:00
} ,
2020-11-13 02:52:38 -06:00
} ,
& setting . Cfg { SendUserHeader : true } ,
)
assert . Equal ( t , "test_user" , req . Header . Get ( "X-Grafana-User" ) )
} )
2019-03-14 07:04:47 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When SendUserHeader config is disabled" , func ( t * testing . T ) {
req := getDatasourceProxiedRequest (
t ,
& models . ReqContext {
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser {
2020-11-13 02:52:38 -06:00
Login : "test_user" ,
2019-03-14 07:04:47 -05:00
} ,
2020-11-13 02:52:38 -06:00
} ,
& setting . Cfg { SendUserHeader : false } ,
)
// Get will return empty string even if header is not set
assert . Empty ( t , req . Header . Get ( "X-Grafana-User" ) )
} )
2019-03-14 10:28:32 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When SendUserHeader config is enabled but user is anonymous" , func ( t * testing . T ) {
req := getDatasourceProxiedRequest (
t ,
& models . ReqContext {
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { IsAnonymous : true } ,
2020-11-13 02:52:38 -06:00
} ,
& setting . Cfg { SendUserHeader : true } ,
)
// Get will return empty string even if header is not set
assert . Empty ( t , req . Header . Get ( "X-Grafana-User" ) )
} )
2019-04-15 04:11:17 -05:00
2020-11-13 02:52:38 -06:00
t . Run ( "When proxying data source proxy should handle authentication" , func ( t * testing . T ) {
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2021-11-04 11:47:21 -05:00
2020-11-13 02:52:38 -06:00
tests := [ ] * testCase {
2022-06-27 11:23:15 -05:00
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB_08 , "http://localhost:9090" , authTypePassword , authCheckQuery ) ,
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB_08 , "http://localhost:9090" , authTypePassword , authCheckQuery ) ,
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB , "http://localhost:9090" , authTypePassword , authCheckHeader ) ,
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB , "http://localhost:9090" , authTypePassword , authCheckHeader ) ,
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB , "http://localhost:9090" , authTypeBasic , authCheckHeader ) ,
createAuthTest ( t , secretsStore , datasources . DS_INFLUXDB , "http://localhost:9090" , authTypeBasic , authCheckHeader ) ,
2020-11-13 02:52:38 -06:00
// These two should be enough for any other datasource at the moment. Proxy has special handling
// only for Influx, others have the same path and only BasicAuth. Non BasicAuth datasources
// do not go through proxy but through TSDB API which is not tested here.
2022-06-27 11:23:15 -05:00
createAuthTest ( t , secretsStore , datasources . DS_ES , "http://localhost:9200" , authTypeBasic , authCheckHeader ) ,
createAuthTest ( t , secretsStore , datasources . DS_ES , "http://localhost:9200" , authTypeBasic , authCheckHeader ) ,
2020-11-13 02:52:38 -06:00
}
for _ , test := range tests {
2022-04-25 11:57:45 -05:00
runDatasourceAuthTest ( t , secretsService , secretsStore , cfg , test )
2020-11-13 02:52:38 -06:00
}
} )
2020-11-13 06:21:43 -06:00
}
// test DataSourceProxy request handling.
func TestDataSourceProxy_requestHandling ( t * testing . T ) {
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2021-05-19 16:53:41 -05:00
httpClientProvider := httpclient . NewProvider ( )
2020-11-13 06:21:43 -06:00
var writeErr error
type setUpCfg struct {
headers map [ string ] string
2021-03-17 06:17:41 -05:00
writeCb func ( w http . ResponseWriter , r * http . Request )
2020-11-13 06:21:43 -06:00
}
2022-06-27 11:23:15 -05:00
setUp := func ( t * testing . T , cfgs ... setUpCfg ) ( * models . ReqContext , * datasources . DataSource ) {
2020-11-13 06:21:43 -06:00
writeErr = nil
2019-05-01 09:32:03 -05:00
2020-11-13 02:52:38 -06:00
backend := httptest . NewServer ( http . HandlerFunc ( func ( w http . ResponseWriter , r * http . Request ) {
http . SetCookie ( w , & http . Cookie { Name : "flavor" , Value : "chocolateChip" } )
2020-11-13 06:21:43 -06:00
written := false
for _ , cfg := range cfgs {
if cfg . writeCb != nil {
t . Log ( "Writing response via callback" )
2021-03-17 06:17:41 -05:00
cfg . writeCb ( w , r )
2020-11-13 06:21:43 -06:00
written = true
}
}
if ! written {
t . Log ( "Writing default response" )
w . WriteHeader ( 200 )
_ , writeErr = w . Write ( [ ] byte ( "I am the backend" ) )
}
2020-11-13 02:52:38 -06:00
} ) )
t . Cleanup ( backend . Close )
2019-05-01 09:32:03 -05:00
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource { Url : backend . URL , Type : datasources . DS_GRAPHITE }
2020-11-13 02:52:38 -06:00
2021-10-11 07:30:59 -05:00
responseWriter := web . NewResponseWriter ( "GET" , httptest . NewRecorder ( ) )
2020-11-13 02:52:38 -06:00
2020-11-13 06:21:43 -06:00
// XXX: Really unsure why, but setting headers within the HTTP handler function doesn't stick,
// so doing it here instead
for _ , cfg := range cfgs {
for k , v := range cfg . headers {
responseWriter . Header ( ) . Set ( k , v )
2019-05-01 09:32:03 -05:00
}
2020-11-13 02:52:38 -06:00
}
2019-05-01 09:32:03 -05:00
2020-11-13 06:21:43 -06:00
return & models . ReqContext {
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { } ,
2021-10-11 07:30:59 -05:00
Context : & web . Context {
2021-09-01 04:18:30 -05:00
Req : httptest . NewRequest ( "GET" , "/render" , nil ) ,
2020-11-13 06:21:43 -06:00
Resp : responseWriter ,
} ,
} , ds
}
2019-10-08 11:57:53 -05:00
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2022-01-20 04:10:12 -06:00
2020-11-13 06:21:43 -06:00
t . Run ( "When response header Set-Cookie is not set should remove proxied Set-Cookie header" , func ( t * testing . T ) {
ctx , ds := setUp ( t )
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/render" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 06:21:43 -06:00
require . NoError ( t , err )
2019-10-08 11:57:53 -05:00
2020-11-13 06:21:43 -06:00
proxy . HandleRequest ( )
require . NoError ( t , writeErr )
assert . Empty ( t , proxy . ctx . Resp . Header ( ) . Get ( "Set-Cookie" ) )
} )
t . Run ( "When response header Set-Cookie is set should remove proxied Set-Cookie header and restore the original Set-Cookie header" , func ( t * testing . T ) {
ctx , ds := setUp ( t , setUpCfg {
headers : map [ string ] string {
"Set-Cookie" : "important_cookie=important_value" ,
} ,
2020-11-13 02:52:38 -06:00
} )
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/render" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 06:21:43 -06:00
require . NoError ( t , err )
2019-05-01 09:32:03 -05:00
2020-11-13 06:21:43 -06:00
proxy . HandleRequest ( )
2019-10-08 11:57:53 -05:00
2020-11-13 06:21:43 -06:00
require . NoError ( t , writeErr )
assert . Equal ( t , "important_cookie=important_value" , proxy . ctx . Resp . Header ( ) . Get ( "Set-Cookie" ) )
} )
2019-10-08 11:57:53 -05:00
2022-02-09 06:44:38 -06:00
t . Run ( "When response should set Content-Security-Policy header" , func ( t * testing . T ) {
ctx , ds := setUp ( t )
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/render" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2022-02-09 06:44:38 -06:00
require . NoError ( t , err )
proxy . HandleRequest ( )
require . NoError ( t , writeErr )
assert . Equal ( t , "sandbox" , proxy . ctx . Resp . Header ( ) . Get ( "Content-Security-Policy" ) )
} )
2020-11-13 06:21:43 -06:00
t . Run ( "Data source returns status code 401" , func ( t * testing . T ) {
ctx , ds := setUp ( t , setUpCfg {
2021-03-17 06:17:41 -05:00
writeCb : func ( w http . ResponseWriter , r * http . Request ) {
2020-11-13 06:21:43 -06:00
w . WriteHeader ( 401 )
w . Header ( ) . Set ( "www-authenticate" , ` Basic realm="Access to the server" ` )
_ , err := w . Write ( [ ] byte ( "Not authenticated" ) )
require . NoError ( t , err )
t . Log ( "Wrote 401 response" )
} ,
2019-05-01 09:32:03 -05:00
} )
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/render" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 06:21:43 -06:00
require . NoError ( t , err )
proxy . HandleRequest ( )
require . NoError ( t , writeErr )
assert . Equal ( t , 400 , proxy . ctx . Resp . Status ( ) , "Status code 401 should be converted to 400" )
assert . Empty ( t , proxy . ctx . Resp . Header ( ) . Get ( "www-authenticate" ) )
2017-08-23 03:52:31 -05:00
} )
2021-03-17 06:17:41 -05:00
t . Run ( "Data source should handle proxy path url encoding correctly" , func ( t * testing . T ) {
var req * http . Request
ctx , ds := setUp ( t , setUpCfg {
writeCb : func ( w http . ResponseWriter , r * http . Request ) {
req = r
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "OK" ) )
require . NoError ( t , err )
} ,
} )
2021-09-01 04:18:30 -05:00
ctx . Req = httptest . NewRequest ( "GET" , "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F" , nil )
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/path/%2Ftest%2Ftest%2F" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2021-03-17 06:17:41 -05:00
require . NoError ( t , err )
proxy . HandleRequest ( )
require . NoError ( t , writeErr )
require . NotNil ( t , req )
require . Equal ( t , "/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F" , req . RequestURI )
} )
2022-01-20 04:10:12 -06:00
t . Run ( "Data source should handle proxy path url encoding correctly with opentelemetry" , func ( t * testing . T ) {
var req * http . Request
ctx , ds := setUp ( t , setUpCfg {
writeCb : func ( w http . ResponseWriter , r * http . Request ) {
req = r
w . WriteHeader ( 200 )
_ , err := w . Write ( [ ] byte ( "OK" ) )
require . NoError ( t , err )
} ,
} )
ctx . Req = httptest . NewRequest ( "GET" , "/api/datasources/proxy/1/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F" , nil )
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "/path/%2Ftest%2Ftest%2F" , & setting . Cfg { } , httpClientProvider , & oauthtoken . Service { } , dsService , tracer )
2022-01-20 04:10:12 -06:00
require . NoError ( t , err )
proxy . HandleRequest ( )
require . NoError ( t , writeErr )
require . NotNil ( t , req )
require . Equal ( t , "/path/%2Ftest%2Ftest%2F?query=%2Ftest%2Ftest%2F" , req . RequestURI )
} )
2017-08-22 10:14:15 -05:00
}
2018-06-12 10:39:38 -05:00
2020-04-22 03:30:06 -05:00
func TestNewDataSourceProxy_InvalidURL ( t * testing . T ) {
ctx := models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleEditor } ,
2020-04-22 03:30:06 -05:00
}
2022-06-27 11:23:15 -05:00
ds := datasources . DataSource {
2020-04-22 03:30:06 -05:00
Type : "test" ,
Url : "://host/root" ,
}
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
var err error
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
_ , err = NewDataSourceProxy ( & ds , routes , & ctx , "api/method" , cfg , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2020-04-22 03:30:06 -05:00
require . Error ( t , err )
2020-10-21 05:39:41 -05:00
assert . True ( t , strings . HasPrefix ( err . Error ( ) , ` validation of data source URL "://host/root" failed ` ) )
2020-05-12 06:04:18 -05:00
}
func TestNewDataSourceProxy_ProtocolLessURL ( t * testing . T ) {
ctx := models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleEditor } ,
2020-05-12 06:04:18 -05:00
}
2022-06-27 11:23:15 -05:00
ds := datasources . DataSource {
2020-05-12 06:04:18 -05:00
Type : "test" ,
Url : "127.0.01:5432" ,
}
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2020-06-17 04:17:11 -05:00
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
_ , err = NewDataSourceProxy ( & ds , routes , & ctx , "api/method" , cfg , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2020-05-12 06:04:18 -05:00
require . NoError ( t , err )
2020-04-22 03:30:06 -05:00
}
2020-06-17 04:17:11 -05:00
// Test wth MSSQL type data sources.
func TestNewDataSourceProxy_MSSQL ( t * testing . T ) {
ctx := models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleEditor } ,
2020-06-17 04:17:11 -05:00
}
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2022-01-20 04:10:12 -06:00
2020-06-17 04:17:11 -05:00
tcs := [ ] struct {
description string
url string
err error
} {
{
description : "Valid ODBC URL" ,
url : ` localhost\instance:1433 ` ,
} ,
{
description : "Invalid ODBC URL" ,
url : ` localhost\instance::1433 ` ,
err : datasource . URLValidationError {
2022-12-01 13:51:12 -06:00
Err : errors . New ( ` unrecognized MSSQL URL format: "localhost\\instance::1433" ` ) ,
2020-06-17 04:17:11 -05:00
URL : ` localhost\instance::1433 ` ,
} ,
} ,
}
for _ , tc := range tcs {
t . Run ( tc . description , func ( t * testing . T ) {
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2022-06-27 11:23:15 -05:00
ds := datasources . DataSource {
2020-06-17 04:17:11 -05:00
Type : "mssql" ,
Url : tc . url ,
}
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
p , err := NewDataSourceProxy ( & ds , routes , & ctx , "api/method" , cfg , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2020-06-17 04:17:11 -05:00
if tc . err == nil {
require . NoError ( t , err )
assert . Equal ( t , & url . URL {
Scheme : "sqlserver" ,
Host : ds . Url ,
} , p . targetUrl )
} else {
require . Error ( t , err )
assert . Equal ( t , tc . err , err )
}
} )
}
}
2019-03-14 07:04:47 -05:00
// getDatasourceProxiedRequest is a helper for easier setup of tests based on global config and ReqContext.
2020-11-13 02:52:38 -06:00
func getDatasourceProxiedRequest ( t * testing . T , ctx * models . ReqContext , cfg * setting . Cfg ) * http . Request {
2022-06-27 11:23:15 -05:00
ds := & datasources . DataSource {
2019-03-14 07:04:47 -05:00
Type : "custom" ,
Url : "http://host/root/" ,
}
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2019-03-14 07:04:47 -05:00
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( ds , routes , ctx , "" , cfg , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2019-03-14 07:04:47 -05:00
req , err := http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2019-03-14 07:04:47 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2019-03-14 07:04:47 -05:00
return req
}
2018-06-14 06:39:46 -05:00
type httpClientStub struct {
2020-11-13 02:52:38 -06:00
t * testing . T
2018-06-12 10:39:38 -05:00
fakeBody [ ] byte
}
2018-06-14 06:39:46 -05:00
func ( c * httpClientStub ) Do ( req * http . Request ) ( * http . Response , error ) {
2020-11-13 02:52:38 -06:00
bodyJSON , err := simplejson . NewJson ( c . fakeBody )
require . NoError ( c . t , err )
2018-06-12 10:39:38 -05:00
_ , passedTokenCacheTest := bodyJSON . CheckGet ( "expires_on" )
2020-11-13 02:52:38 -06:00
require . True ( c . t , passedTokenCacheTest )
2018-06-12 10:39:38 -05:00
bodyJSON . Set ( "expires_on" , fmt . Sprint ( time . Now ( ) . Add ( time . Second * 60 ) . Unix ( ) ) )
2020-11-13 02:52:38 -06:00
body , err := bodyJSON . MarshalJSON ( )
require . NoError ( c . t , err )
2018-06-12 10:39:38 -05:00
resp := & http . Response {
2022-08-10 08:37:51 -05:00
Body : io . NopCloser ( bytes . NewReader ( body ) ) ,
2018-06-12 10:39:38 -05:00
}
return resp , nil
}
2020-11-13 02:52:38 -06:00
func newFakeHTTPClient ( t * testing . T , fakeBody [ ] byte ) httpClient {
2018-06-14 06:39:46 -05:00
return & httpClientStub {
2020-11-13 02:52:38 -06:00
t : t ,
2018-06-14 06:39:46 -05:00
fakeBody : fakeBody ,
2018-06-12 10:39:38 -05:00
}
}
2019-04-15 04:11:17 -05:00
2020-11-13 02:52:38 -06:00
type testCase struct {
2022-06-27 11:23:15 -05:00
datasource * datasources . DataSource
2019-04-15 04:11:17 -05:00
checkReq func ( req * http . Request )
}
const (
2020-11-13 02:52:38 -06:00
authTypePassword = "password"
authTypeBasic = "basic"
2019-04-15 04:11:17 -05:00
)
const (
2020-11-13 02:52:38 -06:00
authCheckQuery = "query"
authCheckHeader = "header"
2019-04-15 04:11:17 -05:00
)
2022-08-25 16:04:44 -05:00
func createAuthTest ( t * testing . T , secretsStore secretskvs . SecretsKVStore , dsType string , url string , authType string , authCheck string ) * testCase {
2019-04-15 04:11:17 -05:00
// Basic user:password
2020-11-13 02:52:38 -06:00
base64AuthHeader := "Basic dXNlcjpwYXNzd29yZA=="
2019-04-15 04:11:17 -05:00
2020-11-13 02:52:38 -06:00
test := & testCase {
2022-06-27 11:23:15 -05:00
datasource : & datasources . DataSource {
2021-05-19 16:53:41 -05:00
Id : 1 ,
2022-04-25 11:57:45 -05:00
OrgId : 1 ,
2022-06-03 10:38:22 -05:00
Name : fmt . Sprintf ( "%s,%s,%s,%s" , dsType , url , authType , authCheck ) ,
2019-04-15 04:11:17 -05:00
Type : dsType ,
JsonData : simplejson . New ( ) ,
2022-01-31 09:39:55 -06:00
Url : url ,
2019-04-15 04:11:17 -05:00
} ,
}
var message string
2021-10-07 09:33:50 -05:00
var err error
2020-11-13 02:52:38 -06:00
if authType == authTypePassword {
2019-04-15 04:11:17 -05:00
message = fmt . Sprintf ( "%v should add username and password" , dsType )
test . datasource . User = "user"
2022-06-03 10:38:22 -05:00
secureJsonData , err := json . Marshal ( map [ string ] string {
"password" : "password" ,
} )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
2022-06-03 10:38:22 -05:00
err = secretsStore . Set ( context . Background ( ) , test . datasource . OrgId , test . datasource . Name , "datasource" , string ( secureJsonData ) )
require . NoError ( t , err )
2019-04-15 04:11:17 -05:00
} else {
message = fmt . Sprintf ( "%v should add basic auth username and password" , dsType )
test . datasource . BasicAuth = true
test . datasource . BasicAuthUser = "user"
2022-06-03 10:38:22 -05:00
secureJsonData , err := json . Marshal ( map [ string ] string {
"basicAuthPassword" : "password" ,
} )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
2022-06-03 10:38:22 -05:00
err = secretsStore . Set ( context . Background ( ) , test . datasource . OrgId , test . datasource . Name , "datasource" , string ( secureJsonData ) )
require . NoError ( t , err )
2019-04-15 04:11:17 -05:00
}
2021-10-07 09:33:50 -05:00
require . NoError ( t , err )
2019-04-15 04:11:17 -05:00
2022-06-03 10:38:22 -05:00
message += " from securejsondata"
2019-04-15 04:11:17 -05:00
2020-11-13 02:52:38 -06:00
if authCheck == authCheckQuery {
2019-04-15 04:11:17 -05:00
message += " to query params"
test . checkReq = func ( req * http . Request ) {
2020-11-13 02:52:38 -06:00
queryVals := req . URL . Query ( )
assert . Equal ( t , "user" , queryVals [ "u" ] [ 0 ] , message )
assert . Equal ( t , "password" , queryVals [ "p" ] [ 0 ] , message )
2019-04-15 04:11:17 -05:00
}
} else {
message += " to auth header"
test . checkReq = func ( req * http . Request ) {
2020-11-13 02:52:38 -06:00
assert . Equal ( t , base64AuthHeader , req . Header . Get ( "Authorization" ) , message )
2019-04-15 04:11:17 -05:00
}
}
return test
}
2022-08-25 16:04:44 -05:00
func runDatasourceAuthTest ( t * testing . T , secretsService secrets . Service , secretsStore secretskvs . SecretsKVStore , cfg * setting . Cfg , test * testCase ) {
2020-03-04 05:57:20 -06:00
ctx := & models . ReqContext { }
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2022-01-20 04:10:12 -06:00
2021-11-01 04:53:33 -05:00
var routes [ ] * plugins . Route
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-04-25 11:57:45 -05:00
proxy , err := NewDataSourceProxy ( test . datasource , routes , ctx , "" , & setting . Cfg { } , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2019-04-15 04:11:17 -05:00
req , err := http . NewRequest ( http . MethodGet , "http://grafana.com/sub" , nil )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2019-04-15 04:11:17 -05:00
2020-11-13 06:21:43 -06:00
proxy . director ( req )
2019-04-15 04:11:17 -05:00
test . checkReq ( req )
}
2021-04-14 12:06:20 -05:00
func Test_PathCheck ( t * testing . T ) {
2022-04-01 06:26:49 -05:00
cfg := & setting . Cfg { }
2021-04-14 12:06:20 -05:00
// Ensure that we test routes appropriately. This test reproduces a historical bug where two routes were defined with different role requirements but the same method and the more privileged route was tested first. Here we ensure auth checks are applied based on the correct route, not just the method.
2021-11-01 04:53:33 -05:00
routes := [ ] * plugins . Route {
{
Path : "a" ,
URL : "https://www.google.com" ,
2022-08-10 04:56:48 -05:00
ReqRole : org . RoleEditor ,
2021-11-01 04:53:33 -05:00
Method : http . MethodGet ,
} ,
{
Path : "b" ,
URL : "https://www.google.com" ,
2022-08-10 04:56:48 -05:00
ReqRole : org . RoleViewer ,
2021-11-01 04:53:33 -05:00
Method : http . MethodGet ,
2021-04-14 12:06:20 -05:00
} ,
}
2022-06-15 05:40:41 -05:00
tracer := tracing . InitializeTracerForTest ( )
2021-11-01 04:53:33 -05:00
2021-04-14 12:06:20 -05:00
setUp := func ( ) ( * models . ReqContext , * http . Request ) {
req , err := http . NewRequest ( "GET" , "http://localhost/asd" , nil )
require . NoError ( t , err )
ctx := & models . ReqContext {
2021-10-11 07:30:59 -05:00
Context : & web . Context { Req : req } ,
2022-08-10 04:56:48 -05:00
SignedInUser : & user . SignedInUser { OrgRole : org . RoleViewer } ,
2021-04-14 12:06:20 -05:00
}
return ctx , req
}
ctx , _ := setUp ( )
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-08-25 16:04:44 -05:00
secretsService := secretsmng . SetupTestService ( t , fakes . NewFakeSecretsStore ( ) )
secretsStore := secretskvs . NewSQLSecretsKVStore ( sqlStore , secretsService , log . New ( "test.logger" ) )
2022-11-14 13:08:10 -06:00
quotaService := quotatest . New ( false , nil )
dsService , err := datasourceservice . ProvideService ( nil , secretsService , secretsStore , cfg , featuremgmt . WithFeatures ( ) , acmock . New ( ) , acmock . NewMockedPermissionsService ( ) , quotaService )
require . NoError ( t , err )
2022-06-27 11:23:15 -05:00
proxy , err := NewDataSourceProxy ( & datasources . DataSource { } , routes , ctx , "b" , & setting . Cfg { } , httpclient . NewProvider ( ) , & oauthtoken . Service { } , dsService , tracer )
2021-04-14 12:06:20 -05:00
require . NoError ( t , err )
require . Nil ( t , proxy . validateRequest ( ) )
2021-11-01 04:53:33 -05:00
require . Equal ( t , routes [ 1 ] , proxy . matchedRoute )
2021-04-14 12:06:20 -05:00
}
2021-07-07 01:54:17 -05:00
type mockOAuthTokenService struct {
token * oauth2 . Token
oAuthEnabled bool
}
2022-08-10 04:56:48 -05:00
func ( m * mockOAuthTokenService ) GetCurrentOAuthToken ( ctx context . Context , user * user . SignedInUser ) * oauth2 . Token {
2021-07-07 01:54:17 -05:00
return m . token
}
2022-06-27 11:23:15 -05:00
func ( m * mockOAuthTokenService ) IsOAuthPassThruEnabled ( ds * datasources . DataSource ) bool {
2021-07-07 01:54:17 -05:00
return m . oAuthEnabled
}
2022-10-18 11:17:28 -05:00
func ( m * mockOAuthTokenService ) HasOAuthEntry ( context . Context , * user . SignedInUser ) ( * models . UserAuth , bool , error ) {
return nil , false , nil
}
func ( m * mockOAuthTokenService ) TryTokenRefresh ( context . Context , * models . UserAuth ) error {
return nil
}
func ( m * mockOAuthTokenService ) InvalidateOAuthTokens ( context . Context , * models . UserAuth ) error {
return nil
}