浏览代码

Added basic auth to data source edit/create, add support for basic auth in data source proxy code, Closes #1510

Torkel Ödegaard 10 年之前
父节点
当前提交
9710771f16

+ 4 - 1
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)

+ 4 - 0
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}

+ 15 - 20
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 {

+ 14 - 12
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 {

+ 8 - 5
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

+ 27 - 19
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:"-"`
 }

+ 31 - 23
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 {

+ 6 - 0
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))
+}

+ 16 - 0
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")
+	})
+}

+ 28 - 1
src/app/features/org/partials/datasourceEdit.html

@@ -41,7 +41,9 @@
 				</ul>
 				<div class="clearfix"></div>
 			</div>
-			<div class="tight-form last">
+
+			<h5>Http settings</h5>
+			<div class="tight-form">
 				<ul class="tight-form-list">
 					<li class="tight-form-item" style="width: 80px">
 						Url
@@ -58,6 +60,31 @@
 				</ul>
 				<div class="clearfix"></div>
 			</div>
+			<div class="tight-form">
+				<ul class="tight-form-list">
+					<li class="tight-form-item" style="width: 80px">
+						Basic Auth
+					</li>
+					<li class="tight-form-item">
+						Enable&nbsp;
+						<input class="cr1" id="current.basicAuth" type="checkbox" ng-model="current.basicAuth" ng-checked="current.basicAuth">
+						<label for="current.basicAuth" class="cr1"></label>
+					</li>
+					<li class="tight-form-item" ng-if="current.basicAuth">
+						User
+					</li>
+					<li ng-if="current.basicAuth">
+						<input type="text" class="tight-form-input input-medium" style="width: 139px" ng-model='current.basicAuthUser' placeholder="user" required></input>
+					</li>
+					<li class="tight-form-item" style="width: 67px" ng-if="current.basicAuth">
+						Password
+					</li>
+					<li ng-if="current.basicAuth">
+						<input type="password" class="tight-form-input input-medium" ng-model='current.basicAuthPassword' placeholder="password" required></input>
+					</li>
+				</ul>
+				<div class="clearfix"></div>
+			</div>
 
 			<div ng-include="datasourceMeta.partials.config" ng-if="datasourceMeta.partials.config"></div>
 			<br>

+ 1 - 1
src/app/plugins/datasource/elasticsearch/datasource.js

@@ -34,7 +34,7 @@ function (angular, _, config, kbn, moment) {
       if (this.basicAuth) {
         options.withCredentials = true;
         options.headers = {
-          "Authorization": "Basic " + this.basicAuth
+          "Authorization": this.basicAuth
         };
       }
 

+ 1 - 1
src/app/plugins/datasource/graphite/datasource.js

@@ -212,7 +212,7 @@ function (angular, _, $, config, kbn, moment) {
       }
       if (this.basicAuth) {
         options.headers = options.headers || {};
-        options.headers.Authorization = 'Basic ' + this.basicAuth;
+        options.headers.Authorization = this.basicAuth;
       }
 
       options.url = this.url + options.url;

+ 1 - 1
src/app/plugins/datasource/influxdb/datasource.js

@@ -181,7 +181,7 @@ function (angular, _, kbn, InfluxSeries, InfluxQueryBuilder) {
 
         options.headers = options.headers || {};
         if (self.basicAuth) {
-          options.headers.Authorization = 'Basic ' + self.basicAuth;
+          options.headers.Authorization = self.basicAuth;
         }
 
         return $http(options).success(function (data) {