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
164242f5
Unverified
Commit
164242f5
authored
May 12, 2020
by
Arve Knudsen
Committed by
GitHub
May 12, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Data sources: Don't fail if URL doesn't specify protocol (#24497)
parent
0475f963
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
193 additions
and
25 deletions
+193
-25
pkg/api/common_test.go
+11
-1
pkg/api/dataproxy.go
+2
-1
pkg/api/datasource/validation.go
+50
-0
pkg/api/datasources.go
+3
-6
pkg/api/datasources_test.go
+105
-0
pkg/api/pluginproxy/ds_proxy.go
+3
-16
pkg/api/pluginproxy/ds_proxy_test.go
+19
-1
No files found.
pkg/api/common_test.go
View file @
164242f5
package
api
package
api
import
(
import
(
"fmt"
"net/http"
"net/http"
"net/http/httptest"
"net/http/httptest"
"path/filepath"
"path/filepath"
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/services/auth"
.
"github.com/smartystreets/goconvey/convey"
.
"github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
"gopkg.in/macaron.v1"
"gopkg.in/macaron.v1"
)
)
...
@@ -82,12 +85,18 @@ func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
...
@@ -82,12 +85,18 @@ func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
func
(
sc
*
scenarioContext
)
fakeReqWithParams
(
method
,
url
string
,
queryParams
map
[
string
]
string
)
*
scenarioContext
{
func
(
sc
*
scenarioContext
)
fakeReqWithParams
(
method
,
url
string
,
queryParams
map
[
string
]
string
)
*
scenarioContext
{
sc
.
resp
=
httptest
.
NewRecorder
()
sc
.
resp
=
httptest
.
NewRecorder
()
req
,
err
:=
http
.
NewRequest
(
method
,
url
,
nil
)
req
,
err
:=
http
.
NewRequest
(
method
,
url
,
nil
)
// TODO: Depend on sc.t
if
sc
.
t
!=
nil
{
require
.
NoError
(
sc
.
t
,
err
)
}
else
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"Making request failed: %s"
,
err
))
}
q
:=
req
.
URL
.
Query
()
q
:=
req
.
URL
.
Query
()
for
k
,
v
:=
range
queryParams
{
for
k
,
v
:=
range
queryParams
{
q
.
Add
(
k
,
v
)
q
.
Add
(
k
,
v
)
}
}
req
.
URL
.
RawQuery
=
q
.
Encode
()
req
.
URL
.
RawQuery
=
q
.
Encode
()
So
(
err
,
ShouldBeNil
)
sc
.
req
=
req
sc
.
req
=
req
return
sc
return
sc
...
@@ -114,6 +123,7 @@ func (sc *scenarioContext) fakeReqNoAssertionsWithCookie(method, url string, coo
...
@@ -114,6 +123,7 @@ func (sc *scenarioContext) fakeReqNoAssertionsWithCookie(method, url string, coo
}
}
type
scenarioContext
struct
{
type
scenarioContext
struct
{
t
*
testing
.
T
m
*
macaron
.
Macaron
m
*
macaron
.
Macaron
context
*
models
.
ReqContext
context
*
models
.
ReqContext
resp
*
httptest
.
ResponseRecorder
resp
*
httptest
.
ResponseRecorder
...
...
pkg/api/dataproxy.go
View file @
164242f5
...
@@ -4,6 +4,7 @@ import (
...
@@ -4,6 +4,7 @@ import (
"errors"
"errors"
"fmt"
"fmt"
"github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/api/pluginproxy"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/infra/metrics"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
...
@@ -37,7 +38,7 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
...
@@ -37,7 +38,7 @@ func (hs *HTTPServer) ProxyDataSourceRequest(c *models.ReqContext) {
proxy
,
err
:=
pluginproxy
.
NewDataSourceProxy
(
ds
,
plugin
,
c
,
proxyPath
,
hs
.
Cfg
)
proxy
,
err
:=
pluginproxy
.
NewDataSourceProxy
(
ds
,
plugin
,
c
,
proxyPath
,
hs
.
Cfg
)
if
err
!=
nil
{
if
err
!=
nil
{
if
errors
.
Is
(
err
,
pluginproxy
.
URLValidationError
{})
{
if
errors
.
Is
(
err
,
datasource
.
URLValidationError
{})
{
c
.
JsonApiErr
(
400
,
fmt
.
Sprintf
(
"Invalid data source URL: %q"
,
ds
.
Url
),
err
)
c
.
JsonApiErr
(
400
,
fmt
.
Sprintf
(
"Invalid data source URL: %q"
,
ds
.
Url
),
err
)
}
else
{
}
else
{
c
.
JsonApiErr
(
500
,
"Failed creating data source proxy"
,
err
)
c
.
JsonApiErr
(
500
,
"Failed creating data source proxy"
,
err
)
...
...
pkg/api/datasource/validation.go
0 → 100644
View file @
164242f5
package
datasource
import
(
"fmt"
"net/url"
"regexp"
"github.com/grafana/grafana/pkg/infra/log"
)
var
logger
=
log
.
New
(
"datasource"
)
// URLValidationError represents an error from validating a data source URL.
type
URLValidationError
struct
{
error
url
string
}
// Error returns the error message.
func
(
e
URLValidationError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Validation of data source URL %q failed: %s"
,
e
.
url
,
e
.
error
.
Error
())
}
// Unwrap returns the wrapped error.
func
(
e
URLValidationError
)
Unwrap
()
error
{
return
e
.
error
}
// reURL is a regexp to detect if a URL specifies the protocol. We match also strings where the actual protocol is
// missing (i.e., "://"), in order to catch these as invalid when parsing.
var
reURL
=
regexp
.
MustCompile
(
"^[^:]*://"
)
// ValidateURL validates a data source URL.
//
// If successful, the valid URL object is returned, otherwise an error is returned.
func
ValidateURL
(
urlStr
string
)
(
*
url
.
URL
,
error
)
{
// Make sure the URL starts with a protocol specifier, so parsing is unambiguous
if
!
reURL
.
MatchString
(
urlStr
)
{
logger
.
Debug
(
"Data source URL doesn't specify protocol, so prepending it with http:// in order to make it unambiguous"
)
urlStr
=
fmt
.
Sprintf
(
"http://%s"
,
urlStr
)
}
u
,
err
:=
url
.
Parse
(
urlStr
)
if
err
!=
nil
{
return
nil
,
URLValidationError
{
error
:
err
,
url
:
urlStr
}
}
return
u
,
nil
}
pkg/api/datasources.go
View file @
164242f5
...
@@ -3,10 +3,10 @@ package api
...
@@ -3,10 +3,10 @@ package api
import
(
import
(
"encoding/json"
"encoding/json"
"fmt"
"fmt"
"net/url"
"sort"
"sort"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana-plugin-sdk-go/backend"
"github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
...
@@ -14,7 +14,6 @@ import (
...
@@ -14,7 +14,6 @@ import (
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/backendplugin"
"github.com/grafana/grafana/pkg/plugins/datasource/wrapper"
"github.com/grafana/grafana/pkg/plugins/datasource/wrapper"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util"
"github.com/grafana/grafana/pkg/util/errutil"
)
)
func
GetDataSources
(
c
*
models
.
ReqContext
)
Response
{
func
GetDataSources
(
c
*
models
.
ReqContext
)
Response
{
...
@@ -131,10 +130,8 @@ func DeleteDataSourceByName(c *models.ReqContext) Response {
...
@@ -131,10 +130,8 @@ func DeleteDataSourceByName(c *models.ReqContext) Response {
func
validateURL
(
u
string
)
Response
{
func
validateURL
(
u
string
)
Response
{
if
u
!=
""
{
if
u
!=
""
{
_
,
err
:=
url
.
Parse
(
u
)
if
_
,
err
:=
datasource
.
ValidateURL
(
u
);
err
!=
nil
{
if
err
!=
nil
{
return
Error
(
400
,
fmt
.
Sprintf
(
"Validation error, invalid URL: %q"
,
u
),
err
)
return
Error
(
400
,
fmt
.
Sprintf
(
"Validation error, invalid URL: %q"
,
u
),
errutil
.
Wrapf
(
err
,
"invalid data source URL %q"
,
u
))
}
}
}
}
...
...
pkg/api/datasources_test.go
View file @
164242f5
...
@@ -5,6 +5,7 @@ import (
...
@@ -5,6 +5,7 @@ import (
"testing"
"testing"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
.
"github.com/smartystreets/goconvey/convey"
.
"github.com/smartystreets/goconvey/convey"
...
@@ -56,3 +57,107 @@ func TestDataSourcesProxy(t *testing.T) {
...
@@ -56,3 +57,107 @@ func TestDataSourcesProxy(t *testing.T) {
})
})
})
})
}
}
// Adding data sources with invalid URLs should lead to an error.
func
TestAddDataSource_InvalidURL
(
t
*
testing
.
T
)
{
defer
bus
.
ClearBusHandlers
()
sc
:=
setupScenarioContext
(
"/api/datasources"
)
// TODO: Make this an argument to setupScenarioContext
sc
.
t
=
t
sc
.
m
.
Post
(
sc
.
url
,
Wrap
(
func
(
c
*
models
.
ReqContext
)
Response
{
return
AddDataSource
(
c
,
models
.
AddDataSourceCommand
{
Name
:
"Test"
,
Url
:
"invalid:url"
,
})
}))
sc
.
fakeReqWithParams
(
"POST"
,
sc
.
url
,
map
[
string
]
string
{})
.
exec
()
assert
.
Equal
(
t
,
400
,
sc
.
resp
.
Code
)
}
// Adding data sources with URLs not specifying protocol should work.
func
TestAddDataSource_URLWithoutProtocol
(
t
*
testing
.
T
)
{
defer
bus
.
ClearBusHandlers
()
const
name
=
"Test"
const
url
=
"localhost:5432"
// Stub handler
bus
.
AddHandler
(
"sql"
,
func
(
cmd
*
models
.
AddDataSourceCommand
)
error
{
assert
.
Equal
(
t
,
name
,
cmd
.
Name
)
assert
.
Equal
(
t
,
url
,
cmd
.
Url
)
cmd
.
Result
=
&
models
.
DataSource
{}
return
nil
})
sc
:=
setupScenarioContext
(
"/api/datasources"
)
// TODO: Make this an argument to setupScenarioContext
sc
.
t
=
t
sc
.
m
.
Post
(
sc
.
url
,
Wrap
(
func
(
c
*
models
.
ReqContext
)
Response
{
return
AddDataSource
(
c
,
models
.
AddDataSourceCommand
{
Name
:
name
,
Url
:
url
,
})
}))
sc
.
fakeReqWithParams
(
"POST"
,
sc
.
url
,
map
[
string
]
string
{})
.
exec
()
assert
.
Equal
(
t
,
200
,
sc
.
resp
.
Code
)
}
// Updating data sources with invalid URLs should lead to an error.
func
TestUpdateDataSource_InvalidURL
(
t
*
testing
.
T
)
{
defer
bus
.
ClearBusHandlers
()
sc
:=
setupScenarioContext
(
"/api/datasources/1234"
)
// TODO: Make this an argument to setupScenarioContext
sc
.
t
=
t
sc
.
m
.
Put
(
sc
.
url
,
Wrap
(
func
(
c
*
models
.
ReqContext
)
Response
{
return
AddDataSource
(
c
,
models
.
AddDataSourceCommand
{
Name
:
"Test"
,
Url
:
"invalid:url"
,
})
}))
sc
.
fakeReqWithParams
(
"PUT"
,
sc
.
url
,
map
[
string
]
string
{})
.
exec
()
assert
.
Equal
(
t
,
400
,
sc
.
resp
.
Code
)
}
// Updating data sources with URLs not specifying protocol should work.
func
TestUpdateDataSource_URLWithoutProtocol
(
t
*
testing
.
T
)
{
defer
bus
.
ClearBusHandlers
()
const
name
=
"Test"
const
url
=
"localhost:5432"
// Stub handler
bus
.
AddHandler
(
"sql"
,
func
(
cmd
*
models
.
AddDataSourceCommand
)
error
{
assert
.
Equal
(
t
,
name
,
cmd
.
Name
)
assert
.
Equal
(
t
,
url
,
cmd
.
Url
)
cmd
.
Result
=
&
models
.
DataSource
{}
return
nil
})
sc
:=
setupScenarioContext
(
"/api/datasources/1234"
)
// TODO: Make this an argument to setupScenarioContext
sc
.
t
=
t
sc
.
m
.
Put
(
sc
.
url
,
Wrap
(
func
(
c
*
models
.
ReqContext
)
Response
{
return
AddDataSource
(
c
,
models
.
AddDataSourceCommand
{
Name
:
name
,
Url
:
url
,
})
}))
sc
.
fakeReqWithParams
(
"PUT"
,
sc
.
url
,
map
[
string
]
string
{})
.
exec
()
assert
.
Equal
(
t
,
200
,
sc
.
resp
.
Code
)
}
pkg/api/pluginproxy/ds_proxy.go
View file @
164242f5
...
@@ -16,6 +16,7 @@ import (
...
@@ -16,6 +16,7 @@ import (
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go"
"golang.org/x/oauth2"
"golang.org/x/oauth2"
"github.com/grafana/grafana/pkg/api/datasource"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/bus"
glog
"github.com/grafana/grafana/pkg/infra/log"
glog
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/login/social"
...
@@ -31,20 +32,6 @@ var (
...
@@ -31,20 +32,6 @@ var (
client
=
newHTTPClient
()
client
=
newHTTPClient
()
)
)
type
URLValidationError
struct
{
error
url
string
}
func
(
e
URLValidationError
)
Error
()
string
{
return
fmt
.
Sprintf
(
"Validation of URL %q failed: %s"
,
e
.
url
,
e
.
error
.
Error
())
}
func
(
e
URLValidationError
)
Unwrap
()
error
{
return
e
.
error
}
type
DataSourceProxy
struct
{
type
DataSourceProxy
struct
{
ds
*
models
.
DataSource
ds
*
models
.
DataSource
ctx
*
models
.
ReqContext
ctx
*
models
.
ReqContext
...
@@ -86,9 +73,9 @@ func (lw *logWrapper) Write(p []byte) (n int, err error) {
...
@@ -86,9 +73,9 @@ func (lw *logWrapper) Write(p []byte) (n int, err error) {
// NewDataSourceProxy creates a new Datasource proxy
// NewDataSourceProxy creates a new Datasource proxy
func
NewDataSourceProxy
(
ds
*
models
.
DataSource
,
plugin
*
plugins
.
DataSourcePlugin
,
ctx
*
models
.
ReqContext
,
func
NewDataSourceProxy
(
ds
*
models
.
DataSource
,
plugin
*
plugins
.
DataSourcePlugin
,
ctx
*
models
.
ReqContext
,
proxyPath
string
,
cfg
*
setting
.
Cfg
)
(
*
DataSourceProxy
,
error
)
{
proxyPath
string
,
cfg
*
setting
.
Cfg
)
(
*
DataSourceProxy
,
error
)
{
targetURL
,
err
:=
url
.
Parse
(
ds
.
Url
)
targetURL
,
err
:=
datasource
.
ValidateURL
(
ds
.
Url
)
if
err
!=
nil
{
if
err
!=
nil
{
return
nil
,
URLValidationError
{
error
:
err
,
url
:
ds
.
Url
}
return
nil
,
err
}
}
return
&
DataSourceProxy
{
return
&
DataSourceProxy
{
...
...
pkg/api/pluginproxy/ds_proxy_test.go
View file @
164242f5
...
@@ -567,7 +567,25 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
...
@@ -567,7 +567,25 @@ func TestNewDataSourceProxy_InvalidURL(t *testing.T) {
plugin
:=
plugins
.
DataSourcePlugin
{}
plugin
:=
plugins
.
DataSourcePlugin
{}
_
,
err
:=
NewDataSourceProxy
(
&
ds
,
&
plugin
,
&
ctx
,
"api/method"
,
&
cfg
)
_
,
err
:=
NewDataSourceProxy
(
&
ds
,
&
plugin
,
&
ctx
,
"api/method"
,
&
cfg
)
require
.
Error
(
t
,
err
)
require
.
Error
(
t
,
err
)
assert
.
True
(
t
,
strings
.
HasPrefix
(
err
.
Error
(),
`Validation of URL "://host/root" failed`
))
assert
.
True
(
t
,
strings
.
HasPrefix
(
err
.
Error
(),
`Validation of data source URL "://host/root" failed`
))
}
func
TestNewDataSourceProxy_ProtocolLessURL
(
t
*
testing
.
T
)
{
ctx
:=
models
.
ReqContext
{
Context
:
&
macaron
.
Context
{
Req
:
macaron
.
Request
{},
},
SignedInUser
:
&
models
.
SignedInUser
{
OrgRole
:
models
.
ROLE_EDITOR
},
}
ds
:=
models
.
DataSource
{
Type
:
"test"
,
Url
:
"127.0.01:5432"
,
}
cfg
:=
setting
.
Cfg
{}
plugin
:=
plugins
.
DataSourcePlugin
{}
_
,
err
:=
NewDataSourceProxy
(
&
ds
,
&
plugin
,
&
ctx
,
"api/method"
,
&
cfg
)
require
.
NoError
(
t
,
err
)
}
}
type
CloseNotifierResponseRecorder
struct
{
type
CloseNotifierResponseRecorder
struct
{
...
...
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