diff --git a/pkg/api/api.go b/pkg/api/api.go index e068cf044ba..8f069c69940 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -73,7 +73,10 @@ func Register(r *macaron.Macaron) { // Data sources r.Group("/datasources", func() { - r.Combo("/").Get(GetDataSources).Put(AddDataSource).Post(bind(m.UpdateDataSourceCommand{}), UpdateDataSource) + r.Combo("/"). + Get(GetDataSources). + Put(bind(m.AddDataSourceCommand{}), AddDataSource). + Post(bind(m.UpdateDataSourceCommand{}), UpdateDataSource) r.Delete("/:id", DeleteDataSource) r.Get("/:id", GetDataSourceById) r.Get("/plugins", GetDataSourcePlugins) diff --git a/pkg/api/dataproxy.go b/pkg/api/dataproxy.go index 3be991068cc..485fd807dbd 100644 --- a/pkg/api/dataproxy.go +++ b/pkg/api/dataproxy.go @@ -35,6 +35,10 @@ func NewReverseProxy(ds *m.DataSource, proxyPath string) *httputil.ReverseProxy } else { req.URL.Path = util.JoinUrlFragments(target.Path, proxyPath) } + + if ds.BasicAuth { + req.Header.Add("Authorization", util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword)) + } } return &httputil.ReverseProxy{Director: director} diff --git a/pkg/api/datasources.go b/pkg/api/datasources.go index e405ffea3e2..76a9bddd253 100644 --- a/pkg/api/datasources.go +++ b/pkg/api/datasources.go @@ -50,18 +50,20 @@ func GetDataSourceById(c *middleware.Context) { ds := query.Result c.JSON(200, &dtos.DataSource{ - Id: ds.Id, - OrgId: ds.OrgId, - Name: ds.Name, - Url: ds.Url, - Type: ds.Type, - Access: ds.Access, - Password: ds.Password, - Database: ds.Database, - User: ds.User, - BasicAuth: ds.BasicAuth, - IsDefault: ds.IsDefault, - JsonData: ds.JsonData, + Id: ds.Id, + OrgId: ds.OrgId, + Name: ds.Name, + Url: ds.Url, + Type: ds.Type, + Access: ds.Access, + Password: ds.Password, + Database: ds.Database, + User: ds.User, + BasicAuth: ds.BasicAuth, + BasicAuthUser: ds.BasicAuthUser, + BasicAuthPassword: ds.BasicAuthPassword, + IsDefault: ds.IsDefault, + JsonData: ds.JsonData, }) } @@ -84,14 +86,7 @@ func DeleteDataSource(c *middleware.Context) { c.JsonOK("Data source deleted") } -func AddDataSource(c *middleware.Context) { - cmd := m.AddDataSourceCommand{} - - if !c.JsonBody(&cmd) { - c.JsonApiErr(400, "Validation failed", nil) - return - } - +func AddDataSource(c *middleware.Context, cmd m.AddDataSourceCommand) { cmd.OrgId = c.OrgId if err := bus.Dispatch(&cmd); err != nil { diff --git a/pkg/api/dtos/models.go b/pkg/api/dtos/models.go index e5540160e1f..c225c6a5bbb 100644 --- a/pkg/api/dtos/models.go +++ b/pkg/api/dtos/models.go @@ -38,18 +38,20 @@ type Dashboard struct { } type DataSource struct { - Id int64 `json:"id"` - OrgId int64 `json:"orgId"` - Name string `json:"name"` - Type string `json:"type"` - Access m.DsAccess `json:"access"` - Url string `json:"url"` - Password string `json:"password"` - User string `json:"user"` - Database string `json:"database"` - BasicAuth bool `json:"basicAuth"` - IsDefault bool `json:"isDefault"` - JsonData map[string]interface{} `json:"jsonData"` + Id int64 `json:"id"` + OrgId int64 `json:"orgId"` + Name string `json:"name"` + Type string `json:"type"` + Access m.DsAccess `json:"access"` + Url string `json:"url"` + Password string `json:"password"` + User string `json:"user"` + Database string `json:"database"` + BasicAuth bool `json:"basicAuth"` + BasicAuthUser string `json:"basicAuthUser"` + BasicAuthPassword string `json:"basicAuthPassword"` + IsDefault bool `json:"isDefault"` + JsonData map[string]interface{} `json:"jsonData"` } type MetricQueryResultDto struct { diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index 315385ed938..89ced4657bd 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -10,6 +10,7 @@ import ( m "github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/plugins" "github.com/grafana/grafana/pkg/setting" + "github.com/grafana/grafana/pkg/util" ) func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, error) { @@ -53,16 +54,18 @@ func getFrontendSettingsMap(c *middleware.Context) (map[string]interface{}, erro defaultDatasource = ds.Name } - if ds.Type == m.DS_INFLUXDB_08 { - if ds.Access == m.DS_ACCESS_DIRECT { + if ds.Access == m.DS_ACCESS_DIRECT { + if ds.BasicAuth { + dsMap["basicAuth"] = util.GetBasicAuthHeader(ds.BasicAuthUser, ds.BasicAuthPassword) + } + + if ds.Type == m.DS_INFLUXDB_08 { dsMap["username"] = ds.User dsMap["password"] = ds.Password dsMap["url"] = url + "/db/" + ds.Database } - } - if ds.Type == m.DS_INFLUXDB { - if ds.Access == m.DS_ACCESS_DIRECT { + if ds.Type == m.DS_INFLUXDB { dsMap["username"] = ds.User dsMap["password"] = ds.Password dsMap["database"] = ds.Database diff --git a/pkg/models/datasource.go b/pkg/models/datasource.go index 5bb6539b3de..2ba236cd56b 100644 --- a/pkg/models/datasource.go +++ b/pkg/models/datasource.go @@ -49,31 +49,39 @@ type DataSource struct { // Also acts as api DTO type AddDataSourceCommand struct { - OrgId int64 `json:"-"` - Name string - Type string - Access DsAccess - Url string - Password string - Database string - User string - IsDefault bool + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + Access DsAccess `json:"access" binding:"Required"` + Url string `json:"url"` + Password string `json:"password"` + Database string `json:"database"` + User string `json:"user"` + BasicAuth bool `json:"basicAuth"` + BasicAuthUser string `json:"basicAuthUser"` + BasicAuthPassword string `json:"basicAuthPassword"` + IsDefault bool `json:"isDefault"` + JsonData map[string]interface{} `json:"jsonData"` + + OrgId int64 `json:"-"` Result *DataSource } // Also acts as api DTO type UpdateDataSourceCommand struct { - Id int64 `json:"id" binding:"Required"` - Name string `json:"name" binding:"Required"` - Type string `json:"type" binding:"Required"` - Access DsAccess `json:"access" binding:"Required"` - Url string `json:"url"` - Password string `json:"password"` - User string `json:"user"` - Database string `json:"database"` - IsDefault bool `json:"isDefault"` - JsonData map[string]interface{} `json:"jsonData"` + Id int64 `json:"id" binding:"Required"` + Name string `json:"name" binding:"Required"` + Type string `json:"type" binding:"Required"` + Access DsAccess `json:"access" binding:"Required"` + Url string `json:"url"` + Password string `json:"password"` + User string `json:"user"` + Database string `json:"database"` + BasicAuth bool `json:"basicAuth"` + BasicAuthUser string `json:"basicAuthUser"` + BasicAuthPassword string `json:"basicAuthPassword"` + IsDefault bool `json:"isDefault"` + JsonData map[string]interface{} `json:"jsonData"` OrgId int64 `json:"-"` } diff --git a/pkg/services/sqlstore/datasource.go b/pkg/services/sqlstore/datasource.go index 41751fcafeb..9c3dd6c2902 100644 --- a/pkg/services/sqlstore/datasource.go +++ b/pkg/services/sqlstore/datasource.go @@ -57,17 +57,21 @@ func AddDataSource(cmd *m.AddDataSourceCommand) error { return inTransaction(func(sess *xorm.Session) error { ds := &m.DataSource{ - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Access: cmd.Access, - Url: cmd.Url, - User: cmd.User, - Password: cmd.Password, - Database: cmd.Database, - IsDefault: cmd.IsDefault, - Created: time.Now(), - Updated: time.Now(), + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Access: cmd.Access, + Url: cmd.Url, + User: cmd.User, + Password: cmd.Password, + Database: cmd.Database, + IsDefault: cmd.IsDefault, + BasicAuth: cmd.BasicAuth, + BasicAuthUser: cmd.BasicAuthUser, + BasicAuthPassword: cmd.BasicAuthPassword, + JsonData: cmd.JsonData, + Created: time.Now(), + Updated: time.Now(), } if _, err := sess.Insert(ds); err != nil { @@ -97,21 +101,25 @@ func UpdateDataSource(cmd *m.UpdateDataSourceCommand) error { return inTransaction(func(sess *xorm.Session) error { ds := &m.DataSource{ - Id: cmd.Id, - OrgId: cmd.OrgId, - Name: cmd.Name, - Type: cmd.Type, - Access: cmd.Access, - Url: cmd.Url, - User: cmd.User, - Password: cmd.Password, - Database: cmd.Database, - IsDefault: cmd.IsDefault, - JsonData: cmd.JsonData, - Updated: time.Now(), + Id: cmd.Id, + OrgId: cmd.OrgId, + Name: cmd.Name, + Type: cmd.Type, + Access: cmd.Access, + Url: cmd.Url, + User: cmd.User, + Password: cmd.Password, + Database: cmd.Database, + IsDefault: cmd.IsDefault, + BasicAuth: cmd.BasicAuth, + BasicAuthUser: cmd.BasicAuthUser, + BasicAuthPassword: cmd.BasicAuthPassword, + JsonData: cmd.JsonData, + Updated: time.Now(), } sess.UseBool("is_default") + sess.UseBool("basic_auth") _, err := sess.Where("id=? and org_id=?", ds.Id, ds.OrgId).Update(ds) if err != nil { diff --git a/pkg/util/encoding.go b/pkg/util/encoding.go index 417ee14f69f..27169133a42 100644 --- a/pkg/util/encoding.go +++ b/pkg/util/encoding.go @@ -5,6 +5,7 @@ import ( "crypto/md5" "crypto/rand" "crypto/sha256" + "encoding/base64" "encoding/hex" "fmt" "hash" @@ -74,3 +75,8 @@ func PBKDF2(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte } return dk[:keyLen] } + +func GetBasicAuthHeader(user string, password string) string { + var userAndPass = user + ":" + password + return "Basic " + base64.StdEncoding.EncodeToString([]byte(userAndPass)) +} diff --git a/pkg/util/encoding_test.go b/pkg/util/encoding_test.go new file mode 100644 index 00000000000..720d9f5be1a --- /dev/null +++ b/pkg/util/encoding_test.go @@ -0,0 +1,16 @@ +package util + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" +) + +func TestEncoding(t *testing.T) { + + Convey("When generating base64 header", t, func() { + result := GetBasicAuthHeader("grafana", "1234") + + So(result, ShouldEqual, "Z3JhZmFuYToxMjM0") + }) +} diff --git a/src/app/features/org/partials/datasourceEdit.html b/src/app/features/org/partials/datasourceEdit.html index 542f6b742e4..f1b0ef46038 100644 --- a/src/app/features/org/partials/datasourceEdit.html +++ b/src/app/features/org/partials/datasourceEdit.html @@ -41,7 +41,9 @@
-