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

This commit is contained in:
Torkel Ödegaard 2015-03-02 09:58:35 +01:00
parent ea1164322b
commit 9710771f16
13 changed files with 156 additions and 84 deletions

View File

@ -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)

View File

@ -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}

View File

@ -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 {

View File

@ -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 {

View File

@ -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

View File

@ -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:"-"`
}

View File

@ -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 {

View File

@ -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
pkg/util/encoding_test.go Normal file
View File

@ -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")
})
}

View File

@ -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>

View File

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

View File

@ -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;

View File

@ -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) {