| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | package userimpl | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-09-28 19:18:19 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2022-09-28 22:17:09 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/events" | 
					
						
							| 
									
										
										
										
											2022-09-27 19:58:49 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/log" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore/db" | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/sqlstore/migrator" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/user" | 
					
						
							| 
									
										
										
										
											2022-09-27 19:58:49 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/setting" | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type store interface { | 
					
						
							|  |  |  | 	Insert(context.Context, *user.User) (int64, error) | 
					
						
							|  |  |  | 	Get(context.Context, *user.User) (*user.User, error) | 
					
						
							| 
									
										
										
										
											2022-08-02 22:58:05 +08:00
										 |  |  | 	GetByID(context.Context, int64) (*user.User, error) | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 	GetNotServiceAccount(context.Context, int64) (*user.User, error) | 
					
						
							|  |  |  | 	Delete(context.Context, int64) error | 
					
						
							| 
									
										
										
										
											2022-08-02 22:58:05 +08:00
										 |  |  | 	CaseInsensitiveLoginConflict(context.Context, string, string) error | 
					
						
							| 
									
										
										
										
											2022-09-28 19:18:19 +08:00
										 |  |  | 	GetByLogin(context.Context, *user.GetUserByLoginQuery) (*user.User, error) | 
					
						
							|  |  |  | 	GetByEmail(context.Context, *user.GetUserByEmailQuery) (*user.User, error) | 
					
						
							| 
									
										
										
										
											2022-09-28 22:17:09 +08:00
										 |  |  | 	Update(context.Context, *user.UpdateUserCommand) error | 
					
						
							|  |  |  | 	ChangePassword(context.Context, *user.ChangeUserPasswordCommand) error | 
					
						
							|  |  |  | 	UpdateLastSeenAt(context.Context, *user.UpdateUserLastSeenAtCommand) error | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type sqlStore struct { | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 	db      db.DB | 
					
						
							|  |  |  | 	dialect migrator.Dialect | 
					
						
							| 
									
										
										
										
											2022-09-27 19:58:49 +08:00
										 |  |  | 	logger  log.Logger | 
					
						
							|  |  |  | 	cfg     *setting.Cfg | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func ProvideStore(db db.DB, cfg *setting.Cfg) sqlStore { | 
					
						
							|  |  |  | 	return sqlStore{ | 
					
						
							|  |  |  | 		db:      db, | 
					
						
							|  |  |  | 		dialect: db.GetDialect(), | 
					
						
							|  |  |  | 		cfg:     cfg, | 
					
						
							|  |  |  | 		logger:  log.New("user.store"), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) Insert(ctx context.Context, cmd *user.User) (int64, error) { | 
					
						
							|  |  |  | 	var userID int64 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	err = ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		sess.UseBool("is_admin") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if userID, err = sess.Insert(cmd); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		sess.PublishAfterCommit(&events.UserCreated{ | 
					
						
							|  |  |  | 			Timestamp: cmd.Created, | 
					
						
							|  |  |  | 			Id:        cmd.ID, | 
					
						
							|  |  |  | 			Name:      cmd.Name, | 
					
						
							|  |  |  | 			Login:     cmd.Login, | 
					
						
							|  |  |  | 			Email:     cmd.Email, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return userID, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | func (ss *sqlStore) Get(ctx context.Context, usr *user.User) (*user.User, error) { | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 		exists, err := sess.Where("email=? OR login=?", usr.Email, usr.Login).Get(usr) | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 		if !exists { | 
					
						
							| 
									
										
										
										
											2022-07-20 20:50:06 +08:00
										 |  |  | 			return user.ErrUserNotFound | 
					
						
							| 
									
										
										
										
											2022-06-28 20:32:25 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return usr, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) Delete(ctx context.Context, userID int64) error { | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		var rawSQL = "DELETE FROM " + ss.dialect.Quote("user") + " WHERE id = ?" | 
					
						
							|  |  |  | 		_, err := sess.Exec(rawSQL, userID) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) GetNotServiceAccount(ctx context.Context, userID int64) (*user.User, error) { | 
					
						
							| 
									
										
										
										
											2022-07-20 20:50:06 +08:00
										 |  |  | 	usr := user.User{ID: userID} | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							| 
									
										
										
										
											2022-07-20 20:50:06 +08:00
										 |  |  | 		has, err := sess.Where(ss.notServiceAccountFilter()).Get(&usr) | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if !has { | 
					
						
							| 
									
										
										
										
											2022-07-20 20:50:06 +08:00
										 |  |  | 			return user.ErrUserNotFound | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							| 
									
										
										
										
											2022-07-20 20:50:06 +08:00
										 |  |  | 	return &usr, err | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 22:58:05 +08:00
										 |  |  | func (ss *sqlStore) GetByID(ctx context.Context, userID int64) (*user.User, error) { | 
					
						
							|  |  |  | 	var usr user.User | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		has, err := sess.ID(&userID). | 
					
						
							|  |  |  | 			Where(ss.notServiceAccountFilter()). | 
					
						
							|  |  |  | 			Get(&usr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} else if !has { | 
					
						
							|  |  |  | 			return user.ErrUserNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return &usr, err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 22:01:05 +08:00
										 |  |  | func (ss *sqlStore) notServiceAccountFilter() string { | 
					
						
							|  |  |  | 	return fmt.Sprintf("%s.is_service_account = %s", | 
					
						
							|  |  |  | 		ss.dialect.Quote("user"), | 
					
						
							|  |  |  | 		ss.dialect.BooleanStr(false)) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-08-02 22:58:05 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) CaseInsensitiveLoginConflict(ctx context.Context, login, email string) error { | 
					
						
							|  |  |  | 	users := make([]user.User, 0) | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		if err := sess.Where("LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)", | 
					
						
							|  |  |  | 			email, login).Find(&users); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if len(users) > 1 { | 
					
						
							|  |  |  | 			return &user.ErrCaseInsensitiveLoginConflict{Users: users} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-28 19:18:19 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) GetByLogin(ctx context.Context, query *user.GetUserByLoginQuery) (*user.User, error) { | 
					
						
							|  |  |  | 	usr := &user.User{} | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		if query.LoginOrEmail == "" { | 
					
						
							|  |  |  | 			return user.ErrUserNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Try and find the user by login first.
 | 
					
						
							|  |  |  | 		// It's not sufficient to assume that a LoginOrEmail with an "@" is an email.
 | 
					
						
							|  |  |  | 		where := "login=?" | 
					
						
							|  |  |  | 		if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 			where = "LOWER(login)=LOWER(?)" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		has, err := sess.Where(ss.notServiceAccountFilter()).Where(where, query.LoginOrEmail).Get(usr) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if !has && strings.Contains(query.LoginOrEmail, "@") { | 
					
						
							|  |  |  | 			// If the user wasn't found, and it contains an "@" fallback to finding the
 | 
					
						
							|  |  |  | 			// user by email.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			where = "email=?" | 
					
						
							|  |  |  | 			if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 				where = "LOWER(email)=LOWER(?)" | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			usr = &user.User{} | 
					
						
							|  |  |  | 			has, err = sess.Where(ss.notServiceAccountFilter()).Where(where, query.LoginOrEmail).Get(usr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} else if !has { | 
					
						
							|  |  |  | 			return user.ErrUserNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 			if err := ss.userCaseInsensitiveLoginConflict(ctx, sess, usr.Login, usr.Email); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return usr, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) GetByEmail(ctx context.Context, query *user.GetUserByEmailQuery) (*user.User, error) { | 
					
						
							|  |  |  | 	usr := &user.User{} | 
					
						
							|  |  |  | 	err := ss.db.WithDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		if query.Email == "" { | 
					
						
							|  |  |  | 			return user.ErrUserNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		where := "email=?" | 
					
						
							|  |  |  | 		if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 			where = "LOWER(email)=LOWER(?)" | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		has, err := sess.Where(ss.notServiceAccountFilter()).Where(where, query.Email).Get(usr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} else if !has { | 
					
						
							|  |  |  | 			return user.ErrUserNotFound | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 			if err := ss.userCaseInsensitiveLoginConflict(ctx, sess, usr.Login, usr.Email); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return usr, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) userCaseInsensitiveLoginConflict(ctx context.Context, sess *sqlstore.DBSession, login, email string) error { | 
					
						
							|  |  |  | 	users := make([]user.User, 0) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := sess.Where("LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)", | 
					
						
							|  |  |  | 		email, login).Find(&users); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(users) > 1 { | 
					
						
							|  |  |  | 		return &user.ErrCaseInsensitiveLoginConflict{Users: users} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-09-28 22:17:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) Update(ctx context.Context, cmd *user.UpdateUserCommand) error { | 
					
						
							|  |  |  | 	if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 		cmd.Login = strings.ToLower(cmd.Login) | 
					
						
							|  |  |  | 		cmd.Email = strings.ToLower(cmd.Email) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		user := user.User{ | 
					
						
							|  |  |  | 			Name:    cmd.Name, | 
					
						
							|  |  |  | 			Email:   cmd.Email, | 
					
						
							|  |  |  | 			Login:   cmd.Login, | 
					
						
							|  |  |  | 			Theme:   cmd.Theme, | 
					
						
							|  |  |  | 			Updated: time.Now(), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if _, err := sess.ID(cmd.UserID).Where(ss.notServiceAccountFilter()).Update(&user); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ss.cfg.CaseInsensitiveLogin { | 
					
						
							|  |  |  | 			if err := ss.userCaseInsensitiveLoginConflict(ctx, sess, user.Login, user.Email); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		sess.PublishAfterCommit(&events.UserUpdated{ | 
					
						
							|  |  |  | 			Timestamp: user.Created, | 
					
						
							|  |  |  | 			Id:        user.ID, | 
					
						
							|  |  |  | 			Name:      user.Name, | 
					
						
							|  |  |  | 			Login:     user.Login, | 
					
						
							|  |  |  | 			Email:     user.Email, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) ChangePassword(ctx context.Context, cmd *user.ChangeUserPasswordCommand) error { | 
					
						
							|  |  |  | 	return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		user := user.User{ | 
					
						
							|  |  |  | 			Password: cmd.NewPassword, | 
					
						
							|  |  |  | 			Updated:  time.Now(), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err := sess.ID(cmd.UserID).Where(ss.notServiceAccountFilter()).Update(&user) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ss *sqlStore) UpdateLastSeenAt(ctx context.Context, cmd *user.UpdateUserLastSeenAtCommand) error { | 
					
						
							|  |  |  | 	return ss.db.WithTransactionalDbSession(ctx, func(sess *sqlstore.DBSession) error { | 
					
						
							|  |  |  | 		user := user.User{ | 
					
						
							|  |  |  | 			ID:         cmd.UserID, | 
					
						
							|  |  |  | 			LastSeenAt: time.Now(), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_, err := sess.ID(cmd.UserID).Update(&user) | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | } |