mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
7d347cd428
* tweaks * remove org count from signedinUser * remove org count from signedinUser store * fix broken tests * restore frontend interface
714 lines
20 KiB
Go
714 lines
20 KiB
Go
package userimpl
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana/pkg/events"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/migrator"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
type store interface {
|
|
Insert(context.Context, *user.User) (int64, error)
|
|
Get(context.Context, *user.User) (*user.User, error)
|
|
GetByID(context.Context, int64) (*user.User, error)
|
|
GetNotServiceAccount(context.Context, int64) (*user.User, error)
|
|
Delete(context.Context, int64) error
|
|
LoginConflict(ctx context.Context, login, email string, caseInsensitive bool) error
|
|
CaseInsensitiveLoginConflict(context.Context, string, string) error
|
|
GetByLogin(context.Context, *user.GetUserByLoginQuery) (*user.User, error)
|
|
GetByEmail(context.Context, *user.GetUserByEmailQuery) (*user.User, error)
|
|
Update(context.Context, *user.UpdateUserCommand) error
|
|
ChangePassword(context.Context, *user.ChangeUserPasswordCommand) error
|
|
UpdateLastSeenAt(context.Context, *user.UpdateUserLastSeenAtCommand) error
|
|
GetSignedInUser(context.Context, *user.GetSignedInUserQuery) (*user.SignedInUser, error)
|
|
UpdateUser(context.Context, *user.User) error
|
|
GetProfile(context.Context, *user.GetUserProfileQuery) (*user.UserProfileDTO, error)
|
|
SetHelpFlag(context.Context, *user.SetUserHelpFlagCommand) error
|
|
UpdatePermissions(context.Context, int64, bool) error
|
|
BatchDisableUsers(context.Context, *user.BatchDisableUsersCommand) error
|
|
Disable(context.Context, *user.DisableUserCommand) error
|
|
Search(context.Context, *user.SearchUsersQuery) (*user.SearchUserQueryResult, error)
|
|
|
|
Count(ctx context.Context) (int64, error)
|
|
}
|
|
|
|
type sqlStore struct {
|
|
db db.DB
|
|
dialect migrator.Dialect
|
|
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"),
|
|
}
|
|
}
|
|
|
|
func (ss *sqlStore) Insert(ctx context.Context, cmd *user.User) (int64, error) {
|
|
var err error
|
|
err = ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
sess.UseBool("is_admin")
|
|
|
|
if _, 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
|
|
}
|
|
|
|
// verify that user was created and cmd.ID was updated with the actual new userID
|
|
_, err = ss.getAnyUserType(ctx, cmd.ID)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return cmd.ID, nil
|
|
}
|
|
|
|
func (ss *sqlStore) Get(ctx context.Context, usr *user.User) (*user.User, error) {
|
|
ret := &user.User{}
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
where := "email=? OR login=?"
|
|
login := usr.Login
|
|
email := usr.Email
|
|
if ss.cfg.CaseInsensitiveLogin {
|
|
where = "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)"
|
|
}
|
|
|
|
exists, err := sess.Where(where, email, login).Get(ret)
|
|
if !exists {
|
|
return user.ErrUserNotFound
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return ret, nil
|
|
}
|
|
|
|
func (ss *sqlStore) Delete(ctx context.Context, userID int64) error {
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) 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) {
|
|
usr := user.User{ID: userID}
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
has, err := sess.Where(ss.notServiceAccountFilter()).Get(&usr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !has {
|
|
return user.ErrUserNotFound
|
|
}
|
|
return nil
|
|
})
|
|
return &usr, err
|
|
}
|
|
|
|
func (ss *sqlStore) GetByID(ctx context.Context, userID int64) (*user.User, error) {
|
|
var usr user.User
|
|
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) 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
|
|
}
|
|
|
|
func (ss *sqlStore) notServiceAccountFilter() string {
|
|
return fmt.Sprintf("%s.is_service_account = %s",
|
|
ss.dialect.Quote("user"),
|
|
ss.dialect.BooleanStr(false))
|
|
}
|
|
|
|
func (ss *sqlStore) CaseInsensitiveLoginConflict(ctx context.Context, login, email string) error {
|
|
users := make([]user.User, 0)
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) 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
|
|
}
|
|
|
|
func (ss *sqlStore) GetByLogin(ctx context.Context, query *user.GetUserByLoginQuery) (*user.User, error) {
|
|
usr := &user.User{}
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
if query.LoginOrEmail == "" {
|
|
return user.ErrUserNotFound
|
|
}
|
|
|
|
var where string
|
|
var has bool
|
|
var err error
|
|
|
|
// Since username can be an email address, attempt login with email address
|
|
// first if the login field has the "@" symbol.
|
|
if strings.Contains(query.LoginOrEmail, "@") {
|
|
where = "email=?"
|
|
if ss.cfg.CaseInsensitiveLogin {
|
|
where = "LOWER(email)=LOWER(?)"
|
|
}
|
|
has, err = sess.Where(ss.notServiceAccountFilter()).Where(where, query.LoginOrEmail).Get(usr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Look for the login field instead of email
|
|
if !has {
|
|
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
|
|
} 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 *db.Session) 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 *db.Session, 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
|
|
}
|
|
|
|
// LoginConflict returns an error if the provided email or login are already
|
|
// associated with a user. If caseInsensitive is true the search is not case
|
|
// sensitive.
|
|
func (ss *sqlStore) LoginConflict(ctx context.Context, login, email string, caseInsensitive bool) error {
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
return ss.loginConflict(ctx, sess, login, email, caseInsensitive)
|
|
})
|
|
return err
|
|
}
|
|
|
|
func (ss *sqlStore) loginConflict(ctx context.Context, sess *db.Session, login, email string, caseInsensitive bool) error {
|
|
users := make([]user.User, 0)
|
|
where := "email=? OR login=?"
|
|
if caseInsensitive {
|
|
where = "LOWER(email)=LOWER(?) OR LOWER(login)=LOWER(?)"
|
|
login = strings.ToLower(login)
|
|
email = strings.ToLower(email)
|
|
}
|
|
|
|
exists, err := sess.Where(where, email, login).Get(&user.User{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if exists {
|
|
return user.ErrUserAlreadyExists
|
|
}
|
|
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
|
|
}
|
|
|
|
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 *db.Session) 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 *db.Session) 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 *db.Session) error {
|
|
user := user.User{
|
|
ID: cmd.UserID,
|
|
LastSeenAt: time.Now(),
|
|
}
|
|
|
|
_, err := sess.ID(cmd.UserID).Update(&user)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (ss *sqlStore) GetSignedInUser(ctx context.Context, query *user.GetSignedInUserQuery) (*user.SignedInUser, error) {
|
|
var signedInUser user.SignedInUser
|
|
err := ss.db.WithDbSession(ctx, func(dbSess *db.Session) error {
|
|
orgId := "u.org_id"
|
|
if query.OrgID > 0 {
|
|
orgId = strconv.FormatInt(query.OrgID, 10)
|
|
}
|
|
|
|
var rawSQL = `SELECT
|
|
u.id as user_id,
|
|
u.is_admin as is_grafana_admin,
|
|
u.email as email,
|
|
u.login as login,
|
|
u.name as name,
|
|
u.is_disabled as is_disabled,
|
|
u.help_flags1 as help_flags1,
|
|
u.last_seen_at as last_seen_at,
|
|
org.name as org_name,
|
|
org_user.role as org_role,
|
|
org.id as org_id,
|
|
u.is_service_account as is_service_account
|
|
FROM ` + ss.dialect.Quote("user") + ` as u
|
|
LEFT OUTER JOIN org_user on org_user.org_id = ` + orgId + ` and org_user.user_id = u.id
|
|
LEFT OUTER JOIN org on org.id = org_user.org_id `
|
|
|
|
sess := dbSess.Table("user")
|
|
sess = sess.Context(ctx)
|
|
switch {
|
|
case query.UserID > 0:
|
|
sess.SQL(rawSQL+"WHERE u.id=?", query.UserID)
|
|
case query.Login != "":
|
|
if ss.cfg.CaseInsensitiveLogin {
|
|
sess.SQL(rawSQL+"WHERE LOWER(u.login)=LOWER(?)", query.Login)
|
|
} else {
|
|
sess.SQL(rawSQL+"WHERE u.login=?", query.Login)
|
|
}
|
|
case query.Email != "":
|
|
if ss.cfg.CaseInsensitiveLogin {
|
|
sess.SQL(rawSQL+"WHERE LOWER(u.email)=LOWER(?)", query.Email)
|
|
} else {
|
|
sess.SQL(rawSQL+"WHERE u.email=?", query.Email)
|
|
}
|
|
}
|
|
has, err := sess.Get(&signedInUser)
|
|
if err != nil {
|
|
return err
|
|
} else if !has {
|
|
return user.ErrUserNotFound
|
|
}
|
|
|
|
if signedInUser.OrgRole == "" {
|
|
signedInUser.OrgID = -1
|
|
signedInUser.OrgName = "Org missing"
|
|
}
|
|
|
|
return nil
|
|
})
|
|
return &signedInUser, err
|
|
}
|
|
|
|
func (ss *sqlStore) UpdateUser(ctx context.Context, user *user.User) error {
|
|
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
_, err := sess.ID(user.ID).Update(user)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (ss *sqlStore) GetProfile(ctx context.Context, query *user.GetUserProfileQuery) (*user.UserProfileDTO, error) {
|
|
var usr user.User
|
|
var userProfile user.UserProfileDTO
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
has, err := sess.ID(query.UserID).Where(ss.notServiceAccountFilter()).Get(&usr)
|
|
|
|
if err != nil {
|
|
return err
|
|
} else if !has {
|
|
return user.ErrUserNotFound
|
|
}
|
|
|
|
userProfile = user.UserProfileDTO{
|
|
ID: usr.ID,
|
|
Name: usr.Name,
|
|
Email: usr.Email,
|
|
Login: usr.Login,
|
|
Theme: usr.Theme,
|
|
IsGrafanaAdmin: usr.IsAdmin,
|
|
IsDisabled: usr.IsDisabled,
|
|
OrgID: usr.OrgID,
|
|
UpdatedAt: usr.Updated,
|
|
CreatedAt: usr.Created,
|
|
}
|
|
|
|
return err
|
|
})
|
|
return &userProfile, err
|
|
}
|
|
|
|
func (ss *sqlStore) SetHelpFlag(ctx context.Context, cmd *user.SetUserHelpFlagCommand) error {
|
|
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
user := user.User{
|
|
ID: cmd.UserID,
|
|
HelpFlags1: cmd.HelpFlags1,
|
|
Updated: time.Now(),
|
|
}
|
|
|
|
_, err := sess.ID(cmd.UserID).Cols("help_flags1").Update(&user)
|
|
return err
|
|
})
|
|
}
|
|
|
|
// UpdatePermissions sets the user Server Admin flag
|
|
func (ss *sqlStore) UpdatePermissions(ctx context.Context, userID int64, isAdmin bool) error {
|
|
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
var user user.User
|
|
if _, err := sess.ID(userID).Where(ss.notServiceAccountFilter()).Get(&user); err != nil {
|
|
return err
|
|
}
|
|
|
|
user.IsAdmin = isAdmin
|
|
sess.UseBool("is_admin")
|
|
_, err := sess.ID(user.ID).Update(&user)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// validate that after update there is at least one server admin
|
|
if err := validateOneAdminLeft(ctx, sess); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func (ss *sqlStore) Count(ctx context.Context) (int64, error) {
|
|
type result struct {
|
|
Count int64
|
|
}
|
|
|
|
r := result{}
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
rawSQL := fmt.Sprintf("SELECT COUNT(*) as count from %s WHERE is_service_account=%s", ss.db.GetDialect().Quote("user"), ss.db.GetDialect().BooleanStr(false))
|
|
if _, err := sess.SQL(rawSQL).Get(&r); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
return r.Count, err
|
|
}
|
|
|
|
// validateOneAdminLeft validate that there is an admin user left
|
|
func validateOneAdminLeft(ctx context.Context, sess *db.Session) error {
|
|
count, err := sess.Where("is_admin=?", true).Count(&user.User{})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if count == 0 {
|
|
return user.ErrLastGrafanaAdmin
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (ss *sqlStore) BatchDisableUsers(ctx context.Context, cmd *user.BatchDisableUsersCommand) error {
|
|
return ss.db.WithTransactionalDbSession(ctx, func(sess *db.Session) error {
|
|
userIds := cmd.UserIDs
|
|
|
|
if len(userIds) == 0 {
|
|
return nil
|
|
}
|
|
|
|
user_id_params := strings.Repeat(",?", len(userIds)-1)
|
|
disableSQL := "UPDATE " + ss.dialect.Quote("user") + " SET is_disabled=? WHERE Id IN (?" + user_id_params + ")"
|
|
|
|
disableParams := []interface{}{disableSQL, cmd.IsDisabled}
|
|
for _, v := range userIds {
|
|
disableParams = append(disableParams, v)
|
|
}
|
|
|
|
_, err := sess.Where(ss.notServiceAccountFilter()).Exec(disableParams...)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (ss *sqlStore) Disable(ctx context.Context, cmd *user.DisableUserCommand) error {
|
|
return ss.db.WithDbSession(ctx, func(dbSess *db.Session) error {
|
|
usr := user.User{}
|
|
sess := dbSess.Table("user")
|
|
|
|
if has, err := sess.ID(cmd.UserID).Where(ss.notServiceAccountFilter()).Get(&usr); err != nil {
|
|
return err
|
|
} else if !has {
|
|
return user.ErrUserNotFound
|
|
}
|
|
|
|
usr.IsDisabled = cmd.IsDisabled
|
|
sess.UseBool("is_disabled")
|
|
|
|
_, err := sess.ID(cmd.UserID).Update(&usr)
|
|
return err
|
|
})
|
|
}
|
|
|
|
func (ss *sqlStore) Search(ctx context.Context, query *user.SearchUsersQuery) (*user.SearchUserQueryResult, error) {
|
|
result := user.SearchUserQueryResult{
|
|
Users: make([]*user.UserSearchHitDTO, 0),
|
|
}
|
|
err := ss.db.WithDbSession(ctx, func(dbSess *db.Session) error {
|
|
queryWithWildcards := "%" + query.Query + "%"
|
|
|
|
whereConditions := make([]string, 0)
|
|
whereParams := make([]interface{}, 0)
|
|
sess := dbSess.Table("user").Alias("u")
|
|
|
|
whereConditions = append(whereConditions, "u.is_service_account = ?")
|
|
whereParams = append(whereParams, ss.dialect.BooleanStr(false))
|
|
|
|
// Join with only most recent auth module
|
|
joinCondition := `(
|
|
SELECT id from user_auth
|
|
WHERE user_auth.user_id = u.id
|
|
ORDER BY user_auth.created DESC `
|
|
joinCondition = "user_auth.id=" + joinCondition + ss.dialect.Limit(1) + ")"
|
|
sess.Join("LEFT", "user_auth", joinCondition)
|
|
if query.OrgID > 0 {
|
|
whereConditions = append(whereConditions, "org_id = ?")
|
|
whereParams = append(whereParams, query.OrgID)
|
|
}
|
|
|
|
// user only sees the users for which it has read permissions
|
|
if !accesscontrol.IsDisabled(ss.cfg) {
|
|
acFilter, err := accesscontrol.Filter(query.SignedInUser, "u.id", "global.users:id:", accesscontrol.ActionUsersRead)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
whereConditions = append(whereConditions, acFilter.Where)
|
|
whereParams = append(whereParams, acFilter.Args...)
|
|
}
|
|
|
|
if query.Query != "" {
|
|
whereConditions = append(whereConditions, "(email "+ss.dialect.LikeStr()+" ? OR name "+ss.dialect.LikeStr()+" ? OR login "+ss.dialect.LikeStr()+" ?)")
|
|
whereParams = append(whereParams, queryWithWildcards, queryWithWildcards, queryWithWildcards)
|
|
}
|
|
|
|
if query.IsDisabled != nil {
|
|
whereConditions = append(whereConditions, "is_disabled = ?")
|
|
whereParams = append(whereParams, query.IsDisabled)
|
|
}
|
|
|
|
if query.AuthModule != "" {
|
|
whereConditions = append(whereConditions, `auth_module=?`)
|
|
whereParams = append(whereParams, query.AuthModule)
|
|
}
|
|
|
|
if len(whereConditions) > 0 {
|
|
sess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
|
}
|
|
|
|
for _, filter := range query.Filters {
|
|
if jc := filter.JoinCondition(); jc != nil {
|
|
sess.Join(jc.Operator, jc.Table, jc.Params)
|
|
}
|
|
if ic := filter.InCondition(); ic != nil {
|
|
sess.In(ic.Condition, ic.Params)
|
|
}
|
|
if wc := filter.WhereCondition(); wc != nil {
|
|
sess.Where(wc.Condition, wc.Params)
|
|
}
|
|
}
|
|
|
|
if query.Limit > 0 {
|
|
offset := query.Limit * (query.Page - 1)
|
|
sess.Limit(query.Limit, offset)
|
|
}
|
|
|
|
sess.Cols("u.id", "u.email", "u.name", "u.login", "u.is_admin", "u.is_disabled", "u.last_seen_at", "user_auth.auth_module")
|
|
sess.Asc("u.login", "u.email")
|
|
if err := sess.Find(&result.Users); err != nil {
|
|
return err
|
|
}
|
|
|
|
// get total
|
|
user := user.User{}
|
|
countSess := dbSess.Table("user").Alias("u")
|
|
|
|
// Join with user_auth table if users filtered by auth_module
|
|
if query.AuthModule != "" {
|
|
countSess.Join("LEFT", "user_auth", joinCondition)
|
|
}
|
|
|
|
if len(whereConditions) > 0 {
|
|
countSess.Where(strings.Join(whereConditions, " AND "), whereParams...)
|
|
}
|
|
|
|
for _, filter := range query.Filters {
|
|
if jc := filter.JoinCondition(); jc != nil {
|
|
countSess.Join(jc.Operator, jc.Table, jc.Params)
|
|
}
|
|
if ic := filter.InCondition(); ic != nil {
|
|
countSess.In(ic.Condition, ic.Params)
|
|
}
|
|
if wc := filter.WhereCondition(); wc != nil {
|
|
countSess.Where(wc.Condition, wc.Params)
|
|
}
|
|
}
|
|
|
|
count, err := countSess.Count(&user)
|
|
result.TotalCount = count
|
|
|
|
for _, user := range result.Users {
|
|
user.LastSeenAtAge = util.GetAgeString(user.LastSeenAt)
|
|
}
|
|
|
|
return err
|
|
})
|
|
return &result, err
|
|
}
|
|
|
|
// getAnyUserType searches for a user record by ID. The user account may be a service account.
|
|
func (ss *sqlStore) getAnyUserType(ctx context.Context, userID int64) (*user.User, error) {
|
|
usr := user.User{ID: userID}
|
|
err := ss.db.WithDbSession(ctx, func(sess *db.Session) error {
|
|
has, err := sess.Get(&usr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !has {
|
|
return user.ErrUserNotFound
|
|
}
|
|
return nil
|
|
})
|
|
return &usr, err
|
|
}
|