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
This commit is contained in:
bergquist
2018-06-18 11:26:31 +02:00
17 changed files with 281 additions and 138 deletions
+2 -1
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"fmt"
"testing"
"time"
@@ -389,7 +390,7 @@ func createUser(name string, role string, isAdmin bool) m.User {
setting.AutoAssignOrgRole = role
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)
q1 := m.GetUserOrgListQuery{UserId: currentUserCmd.Result.Id}
+20 -7
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"testing"
"time"
@@ -22,9 +23,9 @@ func TestAccountDataAccess(t *testing.T) {
ac1cmd := m.CreateUserCommand{Login: "ac1", Email: "ac1@test.com", Name: "ac1 name"}
ac2cmd := m.CreateUserCommand{Login: "ac2", Email: "ac2@test.com", Name: "ac2 name"}
err := CreateUser(&ac1cmd)
err := CreateUser(context.Background(), &ac1cmd)
So(err, ShouldBeNil)
err = CreateUser(&ac2cmd)
err = CreateUser(context.Background(), &ac2cmd)
So(err, ShouldBeNil)
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"}
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)
ac1 := ac1cmd.Result
@@ -150,7 +151,7 @@ func TestAccountDataAccess(t *testing.T) {
})
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)
So(err, ShouldBeNil)
@@ -159,13 +160,25 @@ func TestAccountDataAccess(t *testing.T) {
err := GetSignedInUser(&query)
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.Name, ShouldEqual, "ac2 name")
So(query.Result.Login, ShouldEqual, "ac2")
So(query.Result.OrgName, ShouldEqual, "ac1@test.com")
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() {
@@ -182,7 +195,7 @@ func TestAccountDataAccess(t *testing.T) {
Convey("Given an org user with dashboard permissions", func() {
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)
ac3 := ac3cmd.Result
+62 -2
View File
@@ -20,7 +20,14 @@ func init() {
func AddOrgUser(cmd *m.AddOrgUserCommand) error {
return inTransaction(func(sess *DBSession) error {
// 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
} else if len(res) == 1 {
return m.ErrOrgUserAlreadyAdded
@@ -41,7 +48,26 @@ func AddOrgUser(cmd *m.AddOrgUserCommand) error {
}
_, 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 {
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{
"DELETE FROM org_user 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)
})
}
+14 -5
View File
@@ -22,21 +22,30 @@ func newSession() *DBSession {
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)
var sess *DBSession
sess, ok := value.(*DBSession)
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 {
sess := startSession(ctx)
sess, err := startSession(ctx, x, false)
if err != nil {
return err
}
return callback(sess)
}
+2 -1
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"fmt"
"testing"
@@ -22,7 +23,7 @@ func TestTeamCommandsAndQueries(t *testing.T) {
Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i),
}
err := CreateUser(userCmd)
err := CreateUser(context.Background(), userCmd)
So(err, ShouldBeNil)
userIds = append(userIds, userCmd.Result.Id)
}
+9 -12
View File
@@ -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 {
sess := startSession(ctx)
defer sess.Close()
if err := sess.Begin(); err != nil {
sess, err := startSession(ctx, ss.engine, true)
if err != nil {
return err
}
defer sess.Close()
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
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 {
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
}
defer sess.Close()
err = callback(sess)
// special handling of database locked errors for sqlite, then we can retry 3 times
+1 -5
View File
@@ -5,7 +5,6 @@ import (
"errors"
"testing"
"github.com/grafana/grafana/pkg/log"
"github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
@@ -18,11 +17,9 @@ type testQuery struct {
var ProvokedError = errors.New("testing error.")
func TestTransaction(t *testing.T) {
InitTestDB(t)
ss := InitTestDB(t)
Convey("InTransaction asdf asdf", t, func() {
ss := SqlStore{log: log.New("test-logger")}
cmd := &models.AddApiKeyCommand{Key: "secret-key", Name: "key", OrgId: 1}
err := AddApiKey(cmd)
@@ -50,7 +47,6 @@ func TestTransaction(t *testing.T) {
}
return ProvokedError
})
So(err, ShouldEqual, ProvokedError)
+79 -85
View File
@@ -16,7 +16,7 @@ import (
)
func init() {
bus.AddHandler("sql", CreateUser)
//bus.AddHandler("sql", CreateUser)
bus.AddHandler("sql", GetUserById)
bus.AddHandler("sql", UpdateUser)
bus.AddHandler("sql", ChangeUserPassword)
@@ -31,7 +31,7 @@ func init() {
bus.AddHandler("sql", DeleteUser)
bus.AddHandler("sql", UpdateUserPermissions)
bus.AddHandler("sql", SetUserHelpFlag)
bus.AddHandlerCtx("sql", CreateUserCtx)
bus.AddHandlerCtx("sql", CreateUser)
}
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
}
func internalCreateUser(sess *DBSession, cmd *m.CreateUserCommand) error {
orgId, err := getOrgIdForNewUser(cmd, sess)
if err != nil {
return err
}
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),
}
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")
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,
})
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(),
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 setting.AutoAssignOrg && !user.IsAdmin {
if len(cmd.DefaultOrgRole) > 0 {
orgUser.Role = m.RoleType(cmd.DefaultOrgRole)
} else {
orgUser.Role = m.RoleType(setting.AutoAssignOrgRole)
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),
}
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")
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,
})
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(),
}
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 {
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 {
var user m.User
has, err := x.Id(query.UserId).Get(&user)
+2 -1
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"fmt"
"testing"
@@ -22,7 +23,7 @@ func TestUserAuth(t *testing.T) {
Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i),
}
err = CreateUser(cmd)
err = CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
users = append(users, cmd.Result)
}
+9 -8
View File
@@ -1,6 +1,7 @@
package sqlstore
import (
"context"
"fmt"
"testing"
@@ -24,7 +25,7 @@ func TestUserDataAccess(t *testing.T) {
Name: fmt.Sprint("user", i),
Login: fmt.Sprint("loginuser", i),
}
err = CreateUser(cmd)
err = CreateUser(context.Background(), cmd)
So(err, ShouldBeNil)
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() {
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)
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)
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)
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)
Convey("Should delete connected org users and permissions", func() {
query := &m.GetOrgUsersQuery{OrgId: 1}
query := &m.GetOrgUsersQuery{OrgId: users[0].OrgId}
err = GetOrgUsersForTest(query)
So(err, ShouldBeNil)
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)
So(err, ShouldBeNil)
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)
So(err, ShouldBeNil)