فهرست منبع

Merge branch 'master' into alerting_reminder

* master:
  changelog: add notes about closing #11076
  snapshot: copy correct props when creating a snapshot
  set current org when adding/removing user to org
  changelog: add notes about closing #10707
  Include the vendor directory when copying source in to Docker (#12305)
  changelog: adds note about closing #12199
  adds inTransactionCtx that calls inTransactionWithRetryCtx
  merge create user handlers
  transactions: start sessions and transactions at the same place
  cloudwatch: handle invalid time range
  make sure to use real ip when validating white listed ip's
  changelog: adds note about closing #12286
bergquist 7 سال پیش
والد
کامیت
ffda5efc66

+ 0 - 3
.dockerignore

@@ -11,8 +11,5 @@ dump.rdb
 node_modules
 node_modules
 /local
 /local
 /tmp
 /tmp
-/vendor
 *.yml
 *.yml
 *.md
 *.md
-/vendor
-/tmp

+ 4 - 0
CHANGELOG.md

@@ -12,6 +12,10 @@
 * **Units**: W/m2 (energy), l/h (flow) and kPa (pressure) [#11233](https://github.com/grafana/grafana/pull/11233), thx [@flopp999](https://github.com/flopp999)
 * **Units**: W/m2 (energy), l/h (flow) and kPa (pressure) [#11233](https://github.com/grafana/grafana/pull/11233), thx [@flopp999](https://github.com/flopp999)
 * **Units**: Litre/min (flow) and milliLitre/min (flow) [#12282](https://github.com/grafana/grafana/pull/12282), thx [@flopp999](https://github.com/flopp999)
 * **Units**: Litre/min (flow) and milliLitre/min (flow) [#12282](https://github.com/grafana/grafana/pull/12282), thx [@flopp999](https://github.com/flopp999)
 * **Alerting**: Fix mobile notifications for Microsoft Teams alert notifier [#11484](https://github.com/grafana/grafana/pull/11484), thx [@manacker](https://github.com/manacker)
 * **Alerting**: Fix mobile notifications for Microsoft Teams alert notifier [#11484](https://github.com/grafana/grafana/pull/11484), thx [@manacker](https://github.com/manacker)
+* **Influxdb**: Add support for mode function [#12286](https://github.com/grafana/grafana/issues/12286)
+* **Cloudwatch**: Fixes panic caused by bad timerange settings [#12199](https://github.com/grafana/grafana/issues/12199)
+* **Auth Proxy**: Whitelist proxy IP address instead of client IP address [#10707](https://github.com/grafana/grafana/issues/10707)
+* **User Management**: Make sure that a user always has a current org assigned [#11076](https://github.com/grafana/grafana/issues/11076)
 
 
 # 5.2.0-beta1 (2018-06-05)
 # 5.2.0-beta1 (2018-06-05)
 
 

+ 12 - 8
pkg/middleware/auth_proxy.go

@@ -2,7 +2,6 @@ package middleware
 
 
 import (
 import (
 	"fmt"
 	"fmt"
-	"net"
 	"net/mail"
 	"net/mail"
 	"reflect"
 	"reflect"
 	"strings"
 	"strings"
@@ -29,7 +28,7 @@ func initContextWithAuthProxy(ctx *m.ReqContext, orgID int64) bool {
 	}
 	}
 
 
 	// if auth proxy ip(s) defined, check if request comes from one of those
 	// if auth proxy ip(s) defined, check if request comes from one of those
-	if err := checkAuthenticationProxy(ctx.Req.RemoteAddr, proxyHeaderValue); err != nil {
+	if err := checkAuthenticationProxy(ctx.RemoteAddr(), proxyHeaderValue); err != nil {
 		ctx.Handle(407, "Proxy authentication required", err)
 		ctx.Handle(407, "Proxy authentication required", err)
 		return true
 		return true
 	}
 	}
@@ -197,18 +196,23 @@ func checkAuthenticationProxy(remoteAddr string, proxyHeaderValue string) error
 		return nil
 		return nil
 	}
 	}
 
 
-	proxies := strings.Split(setting.AuthProxyWhitelist, ",")
-	sourceIP, _, err := net.SplitHostPort(remoteAddr)
-	if err != nil {
-		return err
+	// Multiple ip addresses? Right-most IP address is the IP address of the most recent proxy
+	if strings.Contains(remoteAddr, ",") {
+		sourceIPs := strings.Split(remoteAddr, ",")
+		remoteAddr = strings.TrimSpace(sourceIPs[len(sourceIPs)-1])
 	}
 	}
 
 
+	remoteAddr = strings.TrimPrefix(remoteAddr, "[")
+	remoteAddr = strings.TrimSuffix(remoteAddr, "]")
+
+	proxies := strings.Split(setting.AuthProxyWhitelist, ",")
+
 	// Compare allowed IP addresses to actual address
 	// Compare allowed IP addresses to actual address
 	for _, proxyIP := range proxies {
 	for _, proxyIP := range proxies {
-		if sourceIP == strings.TrimSpace(proxyIP) {
+		if remoteAddr == strings.TrimSpace(proxyIP) {
 			return nil
 			return nil
 		}
 		}
 	}
 	}
 
 
-	return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, sourceIP)
+	return fmt.Errorf("Request for user (%s) from %s is not from the authentication proxy", proxyHeaderValue, remoteAddr)
 }
 }

+ 55 - 0
pkg/middleware/middleware_test.go

@@ -293,6 +293,61 @@ func TestMiddlewareContext(t *testing.T) {
 			})
 			})
 		})
 		})
 
 
+		middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is not trusted", func(sc *scenarioContext) {
+			setting.AuthProxyEnabled = true
+			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
+			setting.AuthProxyHeaderProperty = "username"
+			setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
+
+			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+				query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
+				return nil
+			})
+
+			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+				cmd.Result = &m.User{Id: 33}
+				return nil
+			})
+
+			sc.fakeReq("GET", "/")
+			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
+			sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.1, 192.168.1.2")
+			sc.exec()
+
+			Convey("should return 407 status code", func() {
+				So(sc.resp.Code, ShouldEqual, 407)
+				So(sc.resp.Body.String(), ShouldContainSubstring, "Request for user (torkelo) from 192.168.1.2 is not from the authentication proxy")
+			})
+		})
+
+		middlewareScenario("When auth_proxy is enabled and request has X-Forwarded-For that is trusted", func(sc *scenarioContext) {
+			setting.AuthProxyEnabled = true
+			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
+			setting.AuthProxyHeaderProperty = "username"
+			setting.AuthProxyWhitelist = "192.168.1.1, 2001::23"
+
+			bus.AddHandler("test", func(query *m.GetSignedInUserQuery) error {
+				query.Result = &m.SignedInUser{OrgId: 4, UserId: 33}
+				return nil
+			})
+
+			bus.AddHandler("test", func(cmd *m.UpsertUserCommand) error {
+				cmd.Result = &m.User{Id: 33}
+				return nil
+			})
+
+			sc.fakeReq("GET", "/")
+			sc.req.Header.Add("X-WEBAUTH-USER", "torkelo")
+			sc.req.Header.Add("X-Forwarded-For", "client-ip, 192.168.1.2, 192.168.1.1")
+			sc.exec()
+
+			Convey("Should init context with user info", func() {
+				So(sc.context.IsSignedIn, ShouldBeTrue)
+				So(sc.context.UserId, ShouldEqual, 33)
+				So(sc.context.OrgId, ShouldEqual, 4)
+			})
+		})
+
 		middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) {
 		middlewareScenario("When session exists for previous user, create a new session", func(sc *scenarioContext) {
 			setting.AuthProxyEnabled = true
 			setting.AuthProxyEnabled = true
 			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"
 			setting.AuthProxyHeaderName = "X-WEBAUTH-USER"

+ 2 - 1
pkg/services/sqlstore/dashboard_test.go

@@ -1,6 +1,7 @@
 package sqlstore
 package sqlstore
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
 	"time"
 	"time"
@@ -389,7 +390,7 @@ func createUser(name string, role string, isAdmin bool) m.User {
 	setting.AutoAssignOrgRole = role
 	setting.AutoAssignOrgRole = role
 
 
 	currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
 	currentUserCmd := m.CreateUserCommand{Login: name, Email: name + "@test.com", Name: "a " + name, IsAdmin: isAdmin}
-	err := CreateUser(&currentUserCmd)
+	err := CreateUser(context.Background(), &currentUserCmd)
 	So(err, ShouldBeNil)
 	So(err, ShouldBeNil)
 
 
 	q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
 	q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}

+ 20 - 7
pkg/services/sqlstore/org_test.go

@@ -1,6 +1,7 @@
 package sqlstore
 package sqlstore
 
 
 import (
 import (
+	"context"
 	"testing"
 	"testing"
 	"time"
 	"time"
 
 
@@ -22,9 +23,9 @@ func TestAccountDataAccess(t *testing.T) {
 				ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
 				ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
 				ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
 				ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
 
 
-				err := CreateUser(&ac1cmd)
+				err := CreateUser(context.Background(), &ac1cmd)
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
-				err = CreateUser(&ac2cmd)
+				err = CreateUser(context.Background(), &ac2cmd)
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 
 
 				q1 := m.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
 				q1 := m.GetUserOrgListQuery{UserId: ac1cmd.Result.Id}
@@ -43,8 +44,8 @@ func TestAccountDataAccess(t *testing.T) {
 			ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
 			ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
 			ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
 			ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name", IsAdmin: true}
 
 
-			err := CreateUser(&ac1cmd)
-			err = CreateUser(&ac2cmd)
+			err := CreateUser(context.Background(), &ac1cmd)
+			err = CreateUser(context.Background(), &ac2cmd)
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)
 
 
 			ac1 := ac1cmd.Result
 			ac1 := ac1cmd.Result
@@ -150,7 +151,7 @@ func TestAccountDataAccess(t *testing.T) {
 				})
 				})
 
 
 				Convey("Can set using org", func() {
 				Convey("Can set using org", func() {
-					cmd := m.SetUsingOrgCommand{UserId: ac2.Id, OrgId: ac1.Id}
+					cmd := m.SetUsingOrgCommand{UserId: ac2.Id, OrgId: ac1.OrgId}
 					err := SetUsingOrg(&cmd)
 					err := SetUsingOrg(&cmd)
 					So(err, ShouldBeNil)
 					So(err, ShouldBeNil)
 
 
@@ -159,13 +160,25 @@ func TestAccountDataAccess(t *testing.T) {
 						err := GetSignedInUser(&query)
 						err := GetSignedInUser(&query)
 
 
 						So(err, ShouldBeNil)
 						So(err, ShouldBeNil)
-						So(query.Result.OrgId, ShouldEqual, ac1.Id)
+						So(query.Result.OrgId, ShouldEqual, ac1.OrgId)
 						So(query.Result.Email, ShouldEqual, "ac2@test.com")
 						So(query.Result.Email, ShouldEqual, "ac2@test.com")
 						So(query.Result.Name, ShouldEqual, "ac2 name")
 						So(query.Result.Name, ShouldEqual, "ac2 name")
 						So(query.Result.Login, ShouldEqual, "ac2")
 						So(query.Result.Login, ShouldEqual, "ac2")
 						So(query.Result.OrgName, ShouldEqual, "ac1@test.com")
 						So(query.Result.OrgName, ShouldEqual, "ac1@test.com")
 						So(query.Result.OrgRole, ShouldEqual, "Viewer")
 						So(query.Result.OrgRole, ShouldEqual, "Viewer")
 					})
 					})
+
+					Convey("Should set last org as current when removing user from current", func() {
+						remCmd := m.RemoveOrgUserCommand{OrgId: ac1.OrgId, UserId: ac2.Id}
+						err := RemoveOrgUser(&remCmd)
+						So(err, ShouldBeNil)
+
+						query := m.GetSignedInUserQuery{UserId: ac2.Id}
+						err = GetSignedInUser(&query)
+
+						So(err, ShouldBeNil)
+						So(query.Result.OrgId, ShouldEqual, ac2.OrgId)
+					})
 				})
 				})
 
 
 				Convey("Cannot delete last admin org user", func() {
 				Convey("Cannot delete last admin org user", func() {
@@ -182,7 +195,7 @@ func TestAccountDataAccess(t *testing.T) {
 
 
 				Convey("Given an org user with dashboard permissions", func() {
 				Convey("Given an org user with dashboard permissions", func() {
 					ac3cmd := m.CreateUserCommand{Login: "ac3", Email: "ac3@test.com", Name: "ac3 name", IsAdmin: false}
 					ac3cmd := m.CreateUserCommand{Login: "ac3", Email: "ac3@test.com", Name: "ac3 name", IsAdmin: false}
-					err := CreateUser(&ac3cmd)
+					err := CreateUser(context.Background(), &ac3cmd)
 					So(err, ShouldBeNil)
 					So(err, ShouldBeNil)
 					ac3 := ac3cmd.Result
 					ac3 := ac3cmd.Result
 
 

+ 62 - 2
pkg/services/sqlstore/org_users.go

@@ -20,7 +20,14 @@ func init() {
 func AddOrgUser(cmd *m.AddOrgUserCommand) error {
 func AddOrgUser(cmd *m.AddOrgUserCommand) error {
 	return inTransaction(func(sess *DBSession) error {
 	return inTransaction(func(sess *DBSession) error {
 		// check if user exists
 		// check if user exists
-		if res, err := sess.Query("SELECT 1 from org_user WHERE org_id=? and user_id=?", cmd.OrgId, cmd.UserId); err != nil {
+		var user m.User
+		if exists, err := sess.Id(cmd.UserId).Get(&user); err != nil {
+			return err
+		} else if !exists {
+			return m.ErrUserNotFound
+		}
+
+		if res, err := sess.Query("SELECT 1 from org_user WHERE org_id=? and user_id=?", cmd.OrgId, user.Id); err != nil {
 			return err
 			return err
 		} else if len(res) == 1 {
 		} else if len(res) == 1 {
 			return m.ErrOrgUserAlreadyAdded
 			return m.ErrOrgUserAlreadyAdded
@@ -41,7 +48,26 @@ func AddOrgUser(cmd *m.AddOrgUserCommand) error {
 		}
 		}
 
 
 		_, err := sess.Insert(&entity)
 		_, err := sess.Insert(&entity)
-		return err
+		if err != nil {
+			return err
+		}
+
+		var userOrgs []*m.UserOrgDTO
+		sess.Table("org_user")
+		sess.Join("INNER", "org", "org_user.org_id=org.id")
+		sess.Where("org_user.user_id=? AND org_user.org_id=?", user.Id, user.OrgId)
+		sess.Cols("org.name", "org_user.role", "org_user.org_id")
+		err = sess.Find(&userOrgs)
+
+		if err != nil {
+			return err
+		}
+
+		if len(userOrgs) == 0 {
+			return setUsingOrgInTransaction(sess, user.Id, cmd.OrgId)
+		}
+
+		return nil
 	})
 	})
 }
 }
 
 
@@ -110,6 +136,14 @@ func GetOrgUsers(query *m.GetOrgUsersQuery) error {
 
 
 func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
 func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
 	return inTransaction(func(sess *DBSession) error {
 	return inTransaction(func(sess *DBSession) error {
+		// check if user exists
+		var user m.User
+		if exists, err := sess.Id(cmd.UserId).Get(&user); err != nil {
+			return err
+		} else if !exists {
+			return m.ErrUserNotFound
+		}
+
 		deletes := []string{
 		deletes := []string{
 			"DELETE FROM org_user WHERE org_id=? and user_id=?",
 			"DELETE FROM org_user WHERE org_id=? and user_id=?",
 			"DELETE FROM dashboard_acl WHERE org_id=? and user_id = ?",
 			"DELETE FROM dashboard_acl WHERE org_id=? and user_id = ?",
@@ -123,6 +157,32 @@ func RemoveOrgUser(cmd *m.RemoveOrgUserCommand) error {
 			}
 			}
 		}
 		}
 
 
+		var userOrgs []*m.UserOrgDTO
+		sess.Table("org_user")
+		sess.Join("INNER", "org", "org_user.org_id=org.id")
+		sess.Where("org_user.user_id=?", user.Id)
+		sess.Cols("org.name", "org_user.role", "org_user.org_id")
+		err := sess.Find(&userOrgs)
+
+		if err != nil {
+			return err
+		}
+
+		hasCurrentOrgSet := false
+		for _, userOrg := range userOrgs {
+			if user.OrgId == userOrg.OrgId {
+				hasCurrentOrgSet = true
+				break
+			}
+		}
+
+		if !hasCurrentOrgSet && len(userOrgs) > 0 {
+			err = setUsingOrgInTransaction(sess, user.Id, userOrgs[0].OrgId)
+			if err != nil {
+				return err
+			}
+		}
+
 		return validateOneAdminLeftInOrg(cmd.OrgId, sess)
 		return validateOneAdminLeftInOrg(cmd.OrgId, sess)
 	})
 	})
 }
 }

+ 14 - 5
pkg/services/sqlstore/session.go

@@ -22,21 +22,30 @@ func newSession() *DBSession {
 	return &DBSession{Session: x.NewSession()}
 	return &DBSession{Session: x.NewSession()}
 }
 }
 
 
-func startSession(ctx context.Context) *DBSession {
+func startSession(ctx context.Context, engine *xorm.Engine, beginTran bool) (*DBSession, error) {
 	value := ctx.Value(ContextSessionName)
 	value := ctx.Value(ContextSessionName)
 	var sess *DBSession
 	var sess *DBSession
 	sess, ok := value.(*DBSession)
 	sess, ok := value.(*DBSession)
 
 
 	if !ok {
 	if !ok {
-		newSess := newSession()
-		return newSess
+		newSess := &DBSession{Session: engine.NewSession()}
+		if beginTran {
+			err := newSess.Begin()
+			if err != nil {
+				return nil, err
+			}
+		}
+		return newSess, nil
 	}
 	}
 
 
-	return sess
+	return sess, nil
 }
 }
 
 
 func withDbSession(ctx context.Context, callback dbTransactionFunc) error {
 func withDbSession(ctx context.Context, callback dbTransactionFunc) error {
-	sess := startSession(ctx)
+	sess, err := startSession(ctx, x, false)
+	if err != nil {
+		return err
+	}
 
 
 	return callback(sess)
 	return callback(sess)
 }
 }

+ 2 - 1
pkg/services/sqlstore/team_test.go

@@ -1,6 +1,7 @@
 package sqlstore
 package sqlstore
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
 
 
@@ -22,7 +23,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
 					Name:  fmt.Sprint("user", i),
 					Name:  fmt.Sprint("user", i),
 					Login: fmt.Sprint("loginuser", i),
 					Login: fmt.Sprint("loginuser", i),
 				}
 				}
-				err := CreateUser(userCmd)
+				err := CreateUser(context.Background(), userCmd)
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 				userIds = append(userIds, userCmd.Result.Id)
 				userIds = append(userIds, userCmd.Result.Id)
 			}
 			}

+ 9 - 12
pkg/services/sqlstore/transactions.go

@@ -14,16 +14,16 @@ func (ss *SqlStore) InTransaction(ctx context.Context, fn func(ctx context.Conte
 }
 }
 
 
 func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
 func (ss *SqlStore) inTransactionWithRetry(ctx context.Context, fn func(ctx context.Context) error, retry int) error {
-	sess := startSession(ctx)
-	defer sess.Close()
-
-	if err := sess.Begin(); err != nil {
+	sess, err := startSession(ctx, ss.engine, true)
+	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	defer sess.Close()
+
 	withValue := context.WithValue(ctx, ContextSessionName, sess)
 	withValue := context.WithValue(ctx, ContextSessionName, sess)
 
 
-	err := fn(withValue)
+	err = fn(withValue)
 
 
 	// special handling of database locked errors for sqlite, then we can retry 3 times
 	// special handling of database locked errors for sqlite, then we can retry 3 times
 	if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
 	if sqlError, ok := err.(sqlite3.Error); ok && retry < 5 {
@@ -60,16 +60,13 @@ func inTransactionWithRetry(callback dbTransactionFunc, retry int) error {
 }
 }
 
 
 func inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
 func inTransactionWithRetryCtx(ctx context.Context, callback dbTransactionFunc, retry int) error {
-	var err error
-
-	sess := startSession(ctx)
-
-	defer sess.Close()
-
-	if err = sess.Begin(); err != nil {
+	sess, err := startSession(ctx, x, true)
+	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	defer sess.Close()
+
 	err = callback(sess)
 	err = callback(sess)
 
 
 	// special handling of database locked errors for sqlite, then we can retry 3 times
 	// special handling of database locked errors for sqlite, then we can retry 3 times

+ 1 - 5
pkg/services/sqlstore/transactions_test.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"errors"
 	"testing"
 	"testing"
 
 
-	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/models"
 
 
 	. "github.com/smartystreets/goconvey/convey"
 	. "github.com/smartystreets/goconvey/convey"
@@ -18,11 +17,9 @@ type testQuery struct {
 var ProvokedError = errors.New("testing error.")
 var ProvokedError = errors.New("testing error.")
 
 
 func TestTransaction(t *testing.T) {
 func TestTransaction(t *testing.T) {
-	InitTestDB(t)
+	ss := InitTestDB(t)
 
 
 	Convey("InTransaction asdf asdf", t, func() {
 	Convey("InTransaction asdf asdf", t, func() {
-		ss := SqlStore{log: log.New("test-logger")}
-
 		cmd := &models.AddApiKeyCommand{Key: "secret-key", Name: "key", OrgId: 1}
 		cmd := &models.AddApiKeyCommand{Key: "secret-key", Name: "key", OrgId: 1}
 
 
 		err := AddApiKey(cmd)
 		err := AddApiKey(cmd)
@@ -50,7 +47,6 @@ func TestTransaction(t *testing.T) {
 				}
 				}
 
 
 				return ProvokedError
 				return ProvokedError
-
 			})
 			})
 
 
 			So(err, ShouldEqual, ProvokedError)
 			So(err, ShouldEqual, ProvokedError)

+ 71 - 77
pkg/services/sqlstore/user.go

@@ -16,7 +16,7 @@ import (
 )
 )
 
 
 func init() {
 func init() {
-	bus.AddHandler("sql", CreateUser)
+	//bus.AddHandler("sql", CreateUser)
 	bus.AddHandler("sql", GetUserById)
 	bus.AddHandler("sql", GetUserById)
 	bus.AddHandler("sql", UpdateUser)
 	bus.AddHandler("sql", UpdateUser)
 	bus.AddHandler("sql", ChangeUserPassword)
 	bus.AddHandler("sql", ChangeUserPassword)
@@ -31,7 +31,7 @@ func init() {
 	bus.AddHandler("sql", DeleteUser)
 	bus.AddHandler("sql", DeleteUser)
 	bus.AddHandler("sql", UpdateUserPermissions)
 	bus.AddHandler("sql", UpdateUserPermissions)
 	bus.AddHandler("sql", SetUserHelpFlag)
 	bus.AddHandler("sql", SetUserHelpFlag)
-	bus.AddHandlerCtx("sql", CreateUserCtx)
+	bus.AddHandlerCtx("sql", CreateUser)
 }
 }
 
 
 func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *DBSession) (int64, error) {
 func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *DBSession) (int64, error) {
@@ -81,87 +81,77 @@ func getOrgIdForNewUser(cmd *m.CreateUserCommand, sess *DBSession) (int64, error
 	return org.Id, nil
 	return org.Id, nil
 }
 }
 
 
-func internalCreateUser(sess *DBSession, cmd *m.CreateUserCommand) error {
-	orgId, err := getOrgIdForNewUser(cmd, sess)
-	if err != nil {
-		return err
-	}
+func CreateUser(ctx context.Context, cmd *m.CreateUserCommand) error {
+	return inTransactionCtx(ctx, func(sess *DBSession) error {
+		orgId, err := getOrgIdForNewUser(cmd, sess)
+		if err != nil {
+			return err
+		}
 
 
-	if cmd.Email == "" {
-		cmd.Email = cmd.Login
-	}
+		if cmd.Email == "" {
+			cmd.Email = cmd.Login
+		}
 
 
-	// create user
-	user := m.User{
-		Email:         cmd.Email,
-		Name:          cmd.Name,
-		Login:         cmd.Login,
-		Company:       cmd.Company,
-		IsAdmin:       cmd.IsAdmin,
-		OrgId:         orgId,
-		EmailVerified: cmd.EmailVerified,
-		Created:       time.Now(),
-		Updated:       time.Now(),
-		LastSeenAt:    time.Now().AddDate(-10, 0, 0),
-	}
+		// create user
+		user := m.User{
+			Email:         cmd.Email,
+			Name:          cmd.Name,
+			Login:         cmd.Login,
+			Company:       cmd.Company,
+			IsAdmin:       cmd.IsAdmin,
+			OrgId:         orgId,
+			EmailVerified: cmd.EmailVerified,
+			Created:       time.Now(),
+			Updated:       time.Now(),
+			LastSeenAt:    time.Now().AddDate(-10, 0, 0),
+		}
 
 
-	if len(cmd.Password) > 0 {
-		user.Salt = util.GetRandomString(10)
-		user.Rands = util.GetRandomString(10)
-		user.Password = util.EncodePassword(cmd.Password, user.Salt)
-	}
+		if len(cmd.Password) > 0 {
+			user.Salt = util.GetRandomString(10)
+			user.Rands = util.GetRandomString(10)
+			user.Password = util.EncodePassword(cmd.Password, user.Salt)
+		}
 
 
-	sess.UseBool("is_admin")
+		sess.UseBool("is_admin")
 
 
-	if _, err := sess.Insert(&user); err != nil {
-		return err
-	}
+		if _, err := sess.Insert(&user); err != nil {
+			return err
+		}
 
 
-	sess.publishAfterCommit(&events.UserCreated{
-		Timestamp: user.Created,
-		Id:        user.Id,
-		Name:      user.Name,
-		Login:     user.Login,
-		Email:     user.Email,
-	})
+		sess.publishAfterCommit(&events.UserCreated{
+			Timestamp: user.Created,
+			Id:        user.Id,
+			Name:      user.Name,
+			Login:     user.Login,
+			Email:     user.Email,
+		})
 
 
-	cmd.Result = user
+		cmd.Result = user
 
 
-	// create org user link
-	if !cmd.SkipOrgSetup {
-		orgUser := m.OrgUser{
-			OrgId:   orgId,
-			UserId:  user.Id,
-			Role:    m.ROLE_ADMIN,
-			Created: time.Now(),
-			Updated: time.Now(),
-		}
+		// create org user link
+		if !cmd.SkipOrgSetup {
+			orgUser := m.OrgUser{
+				OrgId:   orgId,
+				UserId:  user.Id,
+				Role:    m.ROLE_ADMIN,
+				Created: time.Now(),
+				Updated: time.Now(),
+			}
 
 
-		if setting.AutoAssignOrg && !user.IsAdmin {
-			if len(cmd.DefaultOrgRole) > 0 {
-				orgUser.Role = m.RoleType(cmd.DefaultOrgRole)
-			} else {
-				orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
+			if setting.AutoAssignOrg && !user.IsAdmin {
+				if len(cmd.DefaultOrgRole) > 0 {
+					orgUser.Role = m.RoleType(cmd.DefaultOrgRole)
+				} else {
+					orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
+				}
 			}
 			}
-		}
 
 
-		if _, err = sess.Insert(&orgUser); err != nil {
-			return err
+			if _, err = sess.Insert(&orgUser); err != nil {
+				return err
+			}
 		}
 		}
-	}
-
-	return nil
-}
-
-func CreateUserCtx(ctx context.Context, cmd *m.CreateUserCommand) error {
-	return inTransactionWithRetryCtx(ctx, func(sess *DBSession) error {
-		return internalCreateUser(sess, cmd)
-	}, 0)
-}
 
 
-func CreateUser(cmd *m.CreateUserCommand) error {
-	return inTransaction(func(sess *DBSession) error {
-		return internalCreateUser(sess, cmd)
+		return nil
 	})
 	})
 }
 }
 
 
@@ -302,16 +292,20 @@ func SetUsingOrg(cmd *m.SetUsingOrgCommand) error {
 	}
 	}
 
 
 	return inTransaction(func(sess *DBSession) error {
 	return inTransaction(func(sess *DBSession) error {
-		user := m.User{
-			Id:    cmd.UserId,
-			OrgId: cmd.OrgId,
-		}
-
-		_, err := sess.Id(cmd.UserId).Update(&user)
-		return err
+		return setUsingOrgInTransaction(sess, cmd.UserId, cmd.OrgId)
 	})
 	})
 }
 }
 
 
+func setUsingOrgInTransaction(sess *DBSession, userID int64, orgID int64) error {
+	user := m.User{
+		Id:    userID,
+		OrgId: orgID,
+	}
+
+	_, err := sess.Id(userID).Update(&user)
+	return err
+}
+
 func GetUserProfile(query *m.GetUserProfileQuery) error {
 func GetUserProfile(query *m.GetUserProfileQuery) error {
 	var user m.User
 	var user m.User
 	has, err := x.Id(query.UserId).Get(&user)
 	has, err := x.Id(query.UserId).Get(&user)

+ 2 - 1
pkg/services/sqlstore/user_auth_test.go

@@ -1,6 +1,7 @@
 package sqlstore
 package sqlstore
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
 
 
@@ -22,7 +23,7 @@ func TestUserAuth(t *testing.T) {
 				Name:  fmt.Sprint("user", i),
 				Name:  fmt.Sprint("user", i),
 				Login: fmt.Sprint("loginuser", i),
 				Login: fmt.Sprint("loginuser", i),
 			}
 			}
-			err = CreateUser(cmd)
+			err = CreateUser(context.Background(), cmd)
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)
 			users = append(users, cmd.Result)
 			users = append(users, cmd.Result)
 		}
 		}

+ 9 - 8
pkg/services/sqlstore/user_test.go

@@ -1,6 +1,7 @@
 package sqlstore
 package sqlstore
 
 
 import (
 import (
+	"context"
 	"fmt"
 	"fmt"
 	"testing"
 	"testing"
 
 
@@ -24,7 +25,7 @@ func TestUserDataAccess(t *testing.T) {
 					Name:  fmt.Sprint("user", i),
 					Name:  fmt.Sprint("user", i),
 					Login: fmt.Sprint("loginuser", i),
 					Login: fmt.Sprint("loginuser", i),
 				}
 				}
-				err = CreateUser(cmd)
+				err = CreateUser(context.Background(), cmd)
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 				users = append(users, cmd.Result)
 				users = append(users, cmd.Result)
 			}
 			}
@@ -96,33 +97,33 @@ func TestUserDataAccess(t *testing.T) {
 			})
 			})
 
 
 			Convey("when a user is an org member and has been assigned permissions", func() {
 			Convey("when a user is an org member and has been assigned permissions", func() {
-				err = AddOrgUser(&m.AddOrgUserCommand{LoginOrEmail: users[0].Login, Role: m.ROLE_VIEWER, OrgId: users[0].OrgId})
+				err = AddOrgUser(&m.AddOrgUserCommand{LoginOrEmail: users[1].Login, Role: m.ROLE_VIEWER, OrgId: users[0].OrgId, UserId: users[1].Id})
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 
 
-				testHelperUpdateDashboardAcl(1, m.DashboardAcl{DashboardId: 1, OrgId: users[0].OrgId, UserId: users[0].Id, Permission: m.PERMISSION_EDIT})
+				testHelperUpdateDashboardAcl(1, m.DashboardAcl{DashboardId: 1, OrgId: users[0].OrgId, UserId: users[1].Id, Permission: m.PERMISSION_EDIT})
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 
 
-				err = SavePreferences(&m.SavePreferencesCommand{UserId: users[0].Id, OrgId: users[0].OrgId, HomeDashboardId: 1, Theme: "dark"})
+				err = SavePreferences(&m.SavePreferencesCommand{UserId: users[1].Id, OrgId: users[0].OrgId, HomeDashboardId: 1, Theme: "dark"})
 				So(err, ShouldBeNil)
 				So(err, ShouldBeNil)
 
 
 				Convey("when the user is deleted", func() {
 				Convey("when the user is deleted", func() {
-					err = DeleteUser(&m.DeleteUserCommand{UserId: users[0].Id})
+					err = DeleteUser(&m.DeleteUserCommand{UserId: users[1].Id})
 					So(err, ShouldBeNil)
 					So(err, ShouldBeNil)
 
 
 					Convey("Should delete connected org users and permissions", func() {
 					Convey("Should delete connected org users and permissions", func() {
-						query := &m.GetOrgUsersQuery{OrgId: 1}
+						query := &m.GetOrgUsersQuery{OrgId: users[0].OrgId}
 						err = GetOrgUsersForTest(query)
 						err = GetOrgUsersForTest(query)
 						So(err, ShouldBeNil)
 						So(err, ShouldBeNil)
 
 
 						So(len(query.Result), ShouldEqual, 1)
 						So(len(query.Result), ShouldEqual, 1)
 
 
-						permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: 1}
+						permQuery := &m.GetDashboardAclInfoListQuery{DashboardId: 1, OrgId: users[0].OrgId}
 						err = GetDashboardAclInfoList(permQuery)
 						err = GetDashboardAclInfoList(permQuery)
 						So(err, ShouldBeNil)
 						So(err, ShouldBeNil)
 
 
 						So(len(permQuery.Result), ShouldEqual, 0)
 						So(len(permQuery.Result), ShouldEqual, 0)
 
 
-						prefsQuery := &m.GetPreferencesQuery{OrgId: users[0].OrgId, UserId: users[0].Id}
+						prefsQuery := &m.GetPreferencesQuery{OrgId: users[0].OrgId, UserId: users[1].Id}
 						err = GetPreferences(prefsQuery)
 						err = GetPreferences(prefsQuery)
 						So(err, ShouldBeNil)
 						So(err, ShouldBeNil)
 
 

+ 5 - 0
pkg/tsdb/cloudwatch/cloudwatch.go

@@ -3,6 +3,7 @@ package cloudwatch
 import (
 import (
 	"context"
 	"context"
 	"errors"
 	"errors"
+	"fmt"
 	"regexp"
 	"regexp"
 	"sort"
 	"sort"
 	"strconv"
 	"strconv"
@@ -144,6 +145,10 @@ func (e *CloudWatchExecutor) executeQuery(ctx context.Context, parameters *simpl
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	if endTime.Before(startTime) {
+		return nil, fmt.Errorf("Invalid time range: End time can't be before start time")
+	}
+
 	params := &cloudwatch.GetMetricStatisticsInput{
 	params := &cloudwatch.GetMetricStatisticsInput{
 		Namespace:  aws.String(query.Namespace),
 		Namespace:  aws.String(query.Namespace),
 		MetricName: aws.String(query.MetricName),
 		MetricName: aws.String(query.MetricName),

+ 2 - 0
public/app/features/admin/admin_edit_user_ctrl.ts

@@ -75,6 +75,7 @@ export class AdminEditUserCtrl {
 
 
     $scope.removeOrgUser = function(orgUser) {
     $scope.removeOrgUser = function(orgUser) {
       backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id).then(function() {
       backendSrv.delete('/api/orgs/' + orgUser.orgId + '/users/' + $scope.user_id).then(function() {
+        $scope.getUser($scope.user_id);
         $scope.getUserOrgs($scope.user_id);
         $scope.getUserOrgs($scope.user_id);
       });
       });
     };
     };
@@ -108,6 +109,7 @@ export class AdminEditUserCtrl {
       $scope.newOrg.loginOrEmail = $scope.user.login;
       $scope.newOrg.loginOrEmail = $scope.user.login;
 
 
       backendSrv.post('/api/orgs/' + orgInfo.id + '/users/', $scope.newOrg).then(function() {
       backendSrv.post('/api/orgs/' + orgInfo.id + '/users/', $scope.newOrg).then(function() {
+        $scope.getUser($scope.user_id);
         $scope.getUserOrgs($scope.user_id);
         $scope.getUserOrgs($scope.user_id);
       });
       });
     };
     };

+ 3 - 0
public/app/features/dashboard/share_snapshot_ctrl.ts

@@ -123,6 +123,9 @@ export class ShareSnapshotCtrl {
             enable: annotation.enable,
             enable: annotation.enable,
             iconColor: annotation.iconColor,
             iconColor: annotation.iconColor,
             snapshotData: annotation.snapshotData,
             snapshotData: annotation.snapshotData,
+            type: annotation.type,
+            builtIn: annotation.builtIn,
+            hide: annotation.hide,
           };
           };
         })
         })
         .value();
         .value();