github.com/keys-pub/mattermost-server@v4.10.10+incompatible/store/sqlstore/user_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package sqlstore
     5  
     6  import (
     7  	"database/sql"
     8  	"fmt"
     9  	"net/http"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/mattermost/mattermost-server/einterfaces"
    14  	"github.com/mattermost/mattermost-server/model"
    15  	"github.com/mattermost/mattermost-server/store"
    16  	"github.com/mattermost/mattermost-server/utils"
    17  )
    18  
    19  const (
    20  	PROFILES_IN_CHANNEL_CACHE_SIZE = model.CHANNEL_CACHE_SIZE
    21  	PROFILES_IN_CHANNEL_CACHE_SEC  = 900 // 15 mins
    22  	PROFILE_BY_IDS_CACHE_SIZE      = model.SESSION_CACHE_SIZE
    23  	PROFILE_BY_IDS_CACHE_SEC       = 900 // 15 mins
    24  )
    25  
    26  var (
    27  	USER_SEARCH_TYPE_NAMES_NO_FULL_NAME = []string{"Username", "Nickname"}
    28  	USER_SEARCH_TYPE_NAMES              = []string{"Username", "FirstName", "LastName", "Nickname"}
    29  	USER_SEARCH_TYPE_ALL_NO_FULL_NAME   = []string{"Username", "Nickname", "Email"}
    30  	USER_SEARCH_TYPE_ALL                = []string{"Username", "FirstName", "LastName", "Nickname", "Email"}
    31  )
    32  
    33  type SqlUserStore struct {
    34  	SqlStore
    35  	metrics einterfaces.MetricsInterface
    36  }
    37  
    38  var profilesInChannelCache *utils.Cache = utils.NewLru(PROFILES_IN_CHANNEL_CACHE_SIZE)
    39  var profileByIdsCache *utils.Cache = utils.NewLru(PROFILE_BY_IDS_CACHE_SIZE)
    40  
    41  func (us SqlUserStore) ClearCaches() {
    42  	profilesInChannelCache.Purge()
    43  	profileByIdsCache.Purge()
    44  
    45  	if us.metrics != nil {
    46  		us.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Purge")
    47  		us.metrics.IncrementMemCacheInvalidationCounter("Profile By Ids - Purge")
    48  	}
    49  }
    50  
    51  func (us SqlUserStore) InvalidatProfileCacheForUser(userId string) {
    52  	profileByIdsCache.Remove(userId)
    53  
    54  	if us.metrics != nil {
    55  		us.metrics.IncrementMemCacheInvalidationCounter("Profile By Ids - Remove")
    56  	}
    57  }
    58  
    59  func NewSqlUserStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.UserStore {
    60  	us := &SqlUserStore{
    61  		SqlStore: sqlStore,
    62  		metrics:  metrics,
    63  	}
    64  
    65  	for _, db := range sqlStore.GetAllConns() {
    66  		table := db.AddTableWithName(model.User{}, "Users").SetKeys(false, "Id")
    67  		table.ColMap("Id").SetMaxSize(26)
    68  		table.ColMap("Username").SetMaxSize(64).SetUnique(true)
    69  		table.ColMap("Password").SetMaxSize(128)
    70  		table.ColMap("AuthData").SetMaxSize(128).SetUnique(true)
    71  		table.ColMap("AuthService").SetMaxSize(32)
    72  		table.ColMap("Email").SetMaxSize(128).SetUnique(true)
    73  		table.ColMap("Nickname").SetMaxSize(64)
    74  		table.ColMap("FirstName").SetMaxSize(64)
    75  		table.ColMap("LastName").SetMaxSize(64)
    76  		table.ColMap("Roles").SetMaxSize(256)
    77  		table.ColMap("Props").SetMaxSize(4000)
    78  		table.ColMap("NotifyProps").SetMaxSize(2000)
    79  		table.ColMap("Locale").SetMaxSize(5)
    80  		table.ColMap("MfaSecret").SetMaxSize(128)
    81  		table.ColMap("Position").SetMaxSize(128)
    82  		table.ColMap("Timezone").SetMaxSize(256)
    83  	}
    84  
    85  	return us
    86  }
    87  
    88  func (us SqlUserStore) CreateIndexesIfNotExists() {
    89  	us.CreateIndexIfNotExists("idx_users_email", "Users", "Email")
    90  	us.CreateIndexIfNotExists("idx_users_update_at", "Users", "UpdateAt")
    91  	us.CreateIndexIfNotExists("idx_users_create_at", "Users", "CreateAt")
    92  	us.CreateIndexIfNotExists("idx_users_delete_at", "Users", "DeleteAt")
    93  
    94  	if us.DriverName() == model.DATABASE_DRIVER_POSTGRES {
    95  		us.CreateIndexIfNotExists("idx_users_email_lower", "Users", "lower(Email)")
    96  		us.CreateIndexIfNotExists("idx_users_username_lower", "Users", "lower(Username)")
    97  		us.CreateIndexIfNotExists("idx_users_nickname_lower", "Users", "lower(Nickname)")
    98  		us.CreateIndexIfNotExists("idx_users_firstname_lower", "Users", "lower(FirstName)")
    99  		us.CreateIndexIfNotExists("idx_users_lastname_lower", "Users", "lower(LastName)")
   100  	}
   101  
   102  	us.CreateFullTextIndexIfNotExists("idx_users_all_txt", "Users", strings.Join(USER_SEARCH_TYPE_ALL, ", "))
   103  	us.CreateFullTextIndexIfNotExists("idx_users_all_no_full_name_txt", "Users", strings.Join(USER_SEARCH_TYPE_ALL_NO_FULL_NAME, ", "))
   104  	us.CreateFullTextIndexIfNotExists("idx_users_names_txt", "Users", strings.Join(USER_SEARCH_TYPE_NAMES, ", "))
   105  	us.CreateFullTextIndexIfNotExists("idx_users_names_no_full_name_txt", "Users", strings.Join(USER_SEARCH_TYPE_NAMES_NO_FULL_NAME, ", "))
   106  }
   107  
   108  func (us SqlUserStore) Save(user *model.User) store.StoreChannel {
   109  	return store.Do(func(result *store.StoreResult) {
   110  		if len(user.Id) > 0 {
   111  			result.Err = model.NewAppError("SqlUserStore.Save", "store.sql_user.save.existing.app_error", nil, "user_id="+user.Id, http.StatusBadRequest)
   112  			return
   113  		}
   114  
   115  		user.PreSave()
   116  		if result.Err = user.IsValid(); result.Err != nil {
   117  			return
   118  		}
   119  
   120  		if err := us.GetMaster().Insert(user); err != nil {
   121  			if IsUniqueConstraintError(err, []string{"Email", "users_email_key", "idx_users_email_unique"}) {
   122  				result.Err = model.NewAppError("SqlUserStore.Save", "store.sql_user.save.email_exists.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusBadRequest)
   123  			} else if IsUniqueConstraintError(err, []string{"Username", "users_username_key", "idx_users_username_unique"}) {
   124  				result.Err = model.NewAppError("SqlUserStore.Save", "store.sql_user.save.username_exists.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusBadRequest)
   125  			} else {
   126  				result.Err = model.NewAppError("SqlUserStore.Save", "store.sql_user.save.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusInternalServerError)
   127  			}
   128  		} else {
   129  			result.Data = user
   130  		}
   131  	})
   132  }
   133  
   134  func (us SqlUserStore) Update(user *model.User, trustedUpdateData bool) store.StoreChannel {
   135  	return store.Do(func(result *store.StoreResult) {
   136  		user.PreUpdate()
   137  
   138  		if result.Err = user.IsValid(); result.Err != nil {
   139  			return
   140  		}
   141  
   142  		if oldUserResult, err := us.GetMaster().Get(model.User{}, user.Id); err != nil {
   143  			result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.finding.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusInternalServerError)
   144  		} else if oldUserResult == nil {
   145  			result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.find.app_error", nil, "user_id="+user.Id, http.StatusBadRequest)
   146  		} else {
   147  			oldUser := oldUserResult.(*model.User)
   148  			user.CreateAt = oldUser.CreateAt
   149  			user.AuthData = oldUser.AuthData
   150  			user.AuthService = oldUser.AuthService
   151  			user.Password = oldUser.Password
   152  			user.LastPasswordUpdate = oldUser.LastPasswordUpdate
   153  			user.LastPictureUpdate = oldUser.LastPictureUpdate
   154  			user.EmailVerified = oldUser.EmailVerified
   155  			user.FailedAttempts = oldUser.FailedAttempts
   156  			user.MfaSecret = oldUser.MfaSecret
   157  			user.MfaActive = oldUser.MfaActive
   158  
   159  			if !trustedUpdateData {
   160  				user.Roles = oldUser.Roles
   161  				user.DeleteAt = oldUser.DeleteAt
   162  			}
   163  
   164  			if user.IsOAuthUser() {
   165  				if !trustedUpdateData {
   166  					user.Email = oldUser.Email
   167  				}
   168  			} else if user.IsLDAPUser() && !trustedUpdateData {
   169  				if user.Username != oldUser.Username ||
   170  					user.Email != oldUser.Email {
   171  					result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.can_not_change_ldap.app_error", nil, "user_id="+user.Id, http.StatusBadRequest)
   172  					return
   173  				}
   174  			} else if user.Email != oldUser.Email {
   175  				user.EmailVerified = false
   176  			}
   177  
   178  			if user.Username != oldUser.Username {
   179  				user.UpdateMentionKeysFromUsername(oldUser.Username)
   180  			}
   181  
   182  			if count, err := us.GetMaster().Update(user); err != nil {
   183  				if IsUniqueConstraintError(err, []string{"Email", "users_email_key", "idx_users_email_unique"}) {
   184  					result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.email_taken.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusBadRequest)
   185  				} else if IsUniqueConstraintError(err, []string{"Username", "users_username_key", "idx_users_username_unique"}) {
   186  					result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.username_taken.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusBadRequest)
   187  				} else {
   188  					result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.updating.app_error", nil, "user_id="+user.Id+", "+err.Error(), http.StatusInternalServerError)
   189  				}
   190  			} else if count != 1 {
   191  				result.Err = model.NewAppError("SqlUserStore.Update", "store.sql_user.update.app_error", nil, fmt.Sprintf("user_id=%v, count=%v", user.Id, count), http.StatusInternalServerError)
   192  			} else {
   193  				user.Sanitize(map[string]bool{})
   194  				oldUser.Sanitize(map[string]bool{})
   195  				result.Data = [2]*model.User{user, oldUser}
   196  			}
   197  		}
   198  	})
   199  }
   200  
   201  func (us SqlUserStore) UpdateLastPictureUpdate(userId string) store.StoreChannel {
   202  	return store.Do(func(result *store.StoreResult) {
   203  		curTime := model.GetMillis()
   204  
   205  		if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil {
   206  			result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "store.sql_user.update_last_picture_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
   207  		} else {
   208  			result.Data = userId
   209  		}
   210  	})
   211  }
   212  
   213  func (us SqlUserStore) UpdateUpdateAt(userId string) store.StoreChannel {
   214  	return store.Do(func(result *store.StoreResult) {
   215  		curTime := model.GetMillis()
   216  
   217  		if _, err := us.GetMaster().Exec("UPDATE Users SET UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil {
   218  			result.Err = model.NewAppError("SqlUserStore.UpdateUpdateAt", "store.sql_user.update_update.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
   219  		} else {
   220  			result.Data = userId
   221  		}
   222  	})
   223  }
   224  
   225  func (us SqlUserStore) UpdatePassword(userId, hashedPassword string) store.StoreChannel {
   226  	return store.Do(func(result *store.StoreResult) {
   227  		updateAt := model.GetMillis()
   228  
   229  		if _, err := us.GetMaster().Exec("UPDATE Users SET Password = :Password, LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, AuthData = NULL, AuthService = '', EmailVerified = true, FailedAttempts = 0 WHERE Id = :UserId", map[string]interface{}{"Password": hashedPassword, "LastPasswordUpdate": updateAt, "UpdateAt": updateAt, "UserId": userId}); err != nil {
   230  			result.Err = model.NewAppError("SqlUserStore.UpdatePassword", "store.sql_user.update_password.app_error", nil, "id="+userId+", "+err.Error(), http.StatusInternalServerError)
   231  		} else {
   232  			result.Data = userId
   233  		}
   234  	})
   235  }
   236  
   237  func (us SqlUserStore) UpdateFailedPasswordAttempts(userId string, attempts int) store.StoreChannel {
   238  	return store.Do(func(result *store.StoreResult) {
   239  		if _, err := us.GetMaster().Exec("UPDATE Users SET FailedAttempts = :FailedAttempts WHERE Id = :UserId", map[string]interface{}{"FailedAttempts": attempts, "UserId": userId}); err != nil {
   240  			result.Err = model.NewAppError("SqlUserStore.UpdateFailedPasswordAttempts", "store.sql_user.update_failed_pwd_attempts.app_error", nil, "user_id="+userId, http.StatusInternalServerError)
   241  		} else {
   242  			result.Data = userId
   243  		}
   244  	})
   245  }
   246  
   247  func (us SqlUserStore) UpdateAuthData(userId string, service string, authData *string, email string, resetMfa bool) store.StoreChannel {
   248  	return store.Do(func(result *store.StoreResult) {
   249  		email = strings.ToLower(email)
   250  
   251  		updateAt := model.GetMillis()
   252  
   253  		query := `
   254  			UPDATE
   255  			     Users
   256  			SET
   257  			     Password = '',
   258  			     LastPasswordUpdate = :LastPasswordUpdate,
   259  			     UpdateAt = :UpdateAt,
   260  			     FailedAttempts = 0,
   261  			     AuthService = :AuthService,
   262  			     AuthData = :AuthData`
   263  
   264  		if len(email) != 0 {
   265  			query += ", Email = :Email"
   266  		}
   267  
   268  		if resetMfa {
   269  			query += ", MfaActive = false, MfaSecret = ''"
   270  		}
   271  
   272  		query += " WHERE Id = :UserId"
   273  
   274  		if _, err := us.GetMaster().Exec(query, map[string]interface{}{"LastPasswordUpdate": updateAt, "UpdateAt": updateAt, "UserId": userId, "AuthService": service, "AuthData": authData, "Email": email}); err != nil {
   275  			if IsUniqueConstraintError(err, []string{"Email", "users_email_key", "idx_users_email_unique", "AuthData", "users_authdata_key"}) {
   276  				result.Err = model.NewAppError("SqlUserStore.UpdateAuthData", "store.sql_user.update_auth_data.email_exists.app_error", map[string]interface{}{"Service": service, "Email": email}, "user_id="+userId+", "+err.Error(), http.StatusBadRequest)
   277  			} else {
   278  				result.Err = model.NewAppError("SqlUserStore.UpdateAuthData", "store.sql_user.update_auth_data.app_error", nil, "id="+userId+", "+err.Error(), http.StatusInternalServerError)
   279  			}
   280  		} else {
   281  			result.Data = userId
   282  		}
   283  	})
   284  }
   285  
   286  func (us SqlUserStore) UpdateMfaSecret(userId, secret string) store.StoreChannel {
   287  	return store.Do(func(result *store.StoreResult) {
   288  		updateAt := model.GetMillis()
   289  
   290  		if _, err := us.GetMaster().Exec("UPDATE Users SET MfaSecret = :Secret, UpdateAt = :UpdateAt WHERE Id = :UserId", map[string]interface{}{"Secret": secret, "UpdateAt": updateAt, "UserId": userId}); err != nil {
   291  			result.Err = model.NewAppError("SqlUserStore.UpdateMfaSecret", "store.sql_user.update_mfa_secret.app_error", nil, "id="+userId+", "+err.Error(), http.StatusInternalServerError)
   292  		} else {
   293  			result.Data = userId
   294  		}
   295  	})
   296  }
   297  
   298  func (us SqlUserStore) UpdateMfaActive(userId string, active bool) store.StoreChannel {
   299  	return store.Do(func(result *store.StoreResult) {
   300  		updateAt := model.GetMillis()
   301  
   302  		if _, err := us.GetMaster().Exec("UPDATE Users SET MfaActive = :Active, UpdateAt = :UpdateAt WHERE Id = :UserId", map[string]interface{}{"Active": active, "UpdateAt": updateAt, "UserId": userId}); err != nil {
   303  			result.Err = model.NewAppError("SqlUserStore.UpdateMfaActive", "store.sql_user.update_mfa_active.app_error", nil, "id="+userId+", "+err.Error(), http.StatusInternalServerError)
   304  		} else {
   305  			result.Data = userId
   306  		}
   307  	})
   308  }
   309  
   310  func (us SqlUserStore) Get(id string) store.StoreChannel {
   311  	return store.Do(func(result *store.StoreResult) {
   312  		if obj, err := us.GetReplica().Get(model.User{}, id); err != nil {
   313  			result.Err = model.NewAppError("SqlUserStore.Get", "store.sql_user.get.app_error", nil, "user_id="+id+", "+err.Error(), http.StatusInternalServerError)
   314  		} else if obj == nil {
   315  			result.Err = model.NewAppError("SqlUserStore.Get", store.MISSING_ACCOUNT_ERROR, nil, "user_id="+id, http.StatusNotFound)
   316  		} else {
   317  			result.Data = obj.(*model.User)
   318  		}
   319  	})
   320  }
   321  
   322  func (us SqlUserStore) GetAll() store.StoreChannel {
   323  	return store.Do(func(result *store.StoreResult) {
   324  		var data []*model.User
   325  		if _, err := us.GetReplica().Select(&data, "SELECT * FROM Users"); err != nil {
   326  			result.Err = model.NewAppError("SqlUserStore.GetAll", "store.sql_user.get.app_error", nil, err.Error(), http.StatusInternalServerError)
   327  		}
   328  
   329  		result.Data = data
   330  	})
   331  }
   332  
   333  func (s SqlUserStore) GetEtagForAllProfiles() store.StoreChannel {
   334  	return store.Do(func(result *store.StoreResult) {
   335  		updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users ORDER BY UpdateAt DESC LIMIT 1")
   336  		if err != nil {
   337  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
   338  		} else {
   339  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt)
   340  		}
   341  	})
   342  }
   343  
   344  func (us SqlUserStore) GetAllProfiles(offset int, limit int) store.StoreChannel {
   345  	return store.Do(func(result *store.StoreResult) {
   346  		var users []*model.User
   347  
   348  		if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users ORDER BY Username ASC LIMIT :Limit OFFSET :Offset", map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil {
   349  			result.Err = model.NewAppError("SqlUserStore.GetAllProfiles", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   350  		} else {
   351  
   352  			for _, u := range users {
   353  				u.Sanitize(map[string]bool{})
   354  			}
   355  
   356  			result.Data = users
   357  		}
   358  	})
   359  }
   360  
   361  func (s SqlUserStore) GetEtagForProfiles(teamId string) store.StoreChannel {
   362  	return store.Do(func(result *store.StoreResult) {
   363  		updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"TeamId": teamId})
   364  		if err != nil {
   365  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
   366  		} else {
   367  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt)
   368  		}
   369  	})
   370  }
   371  
   372  func (us SqlUserStore) GetProfiles(teamId string, offset int, limit int) store.StoreChannel {
   373  	return store.Do(func(result *store.StoreResult) {
   374  		var users []*model.User
   375  
   376  		if _, err := us.GetReplica().Select(&users, "SELECT Users.* FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId AND TeamMembers.DeleteAt = 0 ORDER BY Users.Username ASC LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
   377  			result.Err = model.NewAppError("SqlUserStore.GetProfiles", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   378  		} else {
   379  
   380  			for _, u := range users {
   381  				u.Sanitize(map[string]bool{})
   382  			}
   383  
   384  			result.Data = users
   385  		}
   386  	})
   387  }
   388  
   389  func (us SqlUserStore) InvalidateProfilesInChannelCacheByUser(userId string) {
   390  	keys := profilesInChannelCache.Keys()
   391  
   392  	for _, key := range keys {
   393  		if cacheItem, ok := profilesInChannelCache.Get(key); ok {
   394  			userMap := cacheItem.(map[string]*model.User)
   395  			if _, userInCache := userMap[userId]; userInCache {
   396  				profilesInChannelCache.Remove(key)
   397  				if us.metrics != nil {
   398  					us.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Remove by User")
   399  				}
   400  			}
   401  		}
   402  	}
   403  }
   404  
   405  func (us SqlUserStore) InvalidateProfilesInChannelCache(channelId string) {
   406  	profilesInChannelCache.Remove(channelId)
   407  	if us.metrics != nil {
   408  		us.metrics.IncrementMemCacheInvalidationCounter("Profiles in Channel - Remove by Channel")
   409  	}
   410  }
   411  
   412  func (us SqlUserStore) GetProfilesInChannel(channelId string, offset int, limit int) store.StoreChannel {
   413  	return store.Do(func(result *store.StoreResult) {
   414  		var users []*model.User
   415  
   416  		query := `
   417  				SELECT 
   418  					Users.* 
   419  				FROM 
   420  					Users, ChannelMembers 
   421  				WHERE 
   422  					ChannelMembers.ChannelId = :ChannelId 
   423  					AND Users.Id = ChannelMembers.UserId 
   424  				ORDER BY 
   425  					Users.Username ASC 
   426  				LIMIT :Limit OFFSET :Offset
   427  		`
   428  
   429  		if _, err := us.GetReplica().Select(&users, query, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil {
   430  			result.Err = model.NewAppError("SqlUserStore.GetProfilesInChannel", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   431  		} else {
   432  
   433  			for _, u := range users {
   434  				u.Sanitize(map[string]bool{})
   435  			}
   436  
   437  			result.Data = users
   438  		}
   439  	})
   440  }
   441  
   442  func (us SqlUserStore) GetProfilesInChannelByStatus(channelId string, offset int, limit int) store.StoreChannel {
   443  	return store.Do(func(result *store.StoreResult) {
   444  		var users []*model.User
   445  
   446  		query := `
   447  			SELECT 
   448  				Users.*
   449  			FROM Users
   450  				INNER JOIN ChannelMembers ON Users.Id = ChannelMembers.UserId
   451  				LEFT JOIN Status  ON Users.Id = Status.UserId
   452  			WHERE
   453  				ChannelMembers.ChannelId = :ChannelId
   454  			ORDER BY 
   455  				CASE Status
   456  					WHEN 'online' THEN 1
   457  					WHEN 'away' THEN 2
   458  					WHEN 'dnd' THEN 3
   459  					ELSE 4
   460  				END,
   461  				Users.Username ASC 
   462  			LIMIT :Limit OFFSET :Offset
   463  		`
   464  
   465  		if _, err := us.GetReplica().Select(&users, query, map[string]interface{}{"ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil {
   466  			result.Err = model.NewAppError("SqlUserStore.GetProfilesInChannelByStatus", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   467  		} else {
   468  
   469  			for _, u := range users {
   470  				u.Sanitize(map[string]bool{})
   471  			}
   472  
   473  			result.Data = users
   474  		}
   475  	})
   476  }
   477  
   478  func (us SqlUserStore) GetAllProfilesInChannel(channelId string, allowFromCache bool) store.StoreChannel {
   479  	return store.Do(func(result *store.StoreResult) {
   480  		if allowFromCache {
   481  			if cacheItem, ok := profilesInChannelCache.Get(channelId); ok {
   482  				if us.metrics != nil {
   483  					us.metrics.IncrementMemCacheHitCounter("Profiles in Channel")
   484  				}
   485  				result.Data = cacheItem.(map[string]*model.User)
   486  				return
   487  			} else {
   488  				if us.metrics != nil {
   489  					us.metrics.IncrementMemCacheMissCounter("Profiles in Channel")
   490  				}
   491  			}
   492  		} else {
   493  			if us.metrics != nil {
   494  				us.metrics.IncrementMemCacheMissCounter("Profiles in Channel")
   495  			}
   496  		}
   497  
   498  		var users []*model.User
   499  
   500  		query := "SELECT Users.* FROM Users, ChannelMembers WHERE ChannelMembers.ChannelId = :ChannelId AND Users.Id = ChannelMembers.UserId AND Users.DeleteAt = 0"
   501  
   502  		if _, err := us.GetReplica().Select(&users, query, map[string]interface{}{"ChannelId": channelId}); err != nil {
   503  			result.Err = model.NewAppError("SqlUserStore.GetAllProfilesInChannel", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   504  		} else {
   505  
   506  			userMap := make(map[string]*model.User)
   507  
   508  			for _, u := range users {
   509  				u.Sanitize(map[string]bool{})
   510  				userMap[u.Id] = u
   511  			}
   512  
   513  			result.Data = userMap
   514  
   515  			if allowFromCache {
   516  				profilesInChannelCache.AddWithExpiresInSecs(channelId, userMap, PROFILES_IN_CHANNEL_CACHE_SEC)
   517  			}
   518  		}
   519  	})
   520  }
   521  
   522  func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int) store.StoreChannel {
   523  	return store.Do(func(result *store.StoreResult) {
   524  		var users []*model.User
   525  
   526  		if _, err := us.GetReplica().Select(&users, `
   527              SELECT
   528                  u.*
   529              FROM Users u
   530              INNER JOIN TeamMembers tm
   531                  ON tm.UserId = u.Id
   532                  AND tm.TeamId = :TeamId
   533                  AND tm.DeleteAt = 0
   534              LEFT JOIN ChannelMembers cm
   535                  ON cm.UserId = u.Id
   536                  AND cm.ChannelId = :ChannelId
   537              WHERE cm.UserId IS NULL
   538              ORDER BY u.Username ASC
   539              LIMIT :Limit OFFSET :Offset
   540              `, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId, "Offset": offset, "Limit": limit}); err != nil {
   541  			result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInChannel", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   542  		} else {
   543  
   544  			for _, u := range users {
   545  				u.Sanitize(map[string]bool{})
   546  			}
   547  
   548  			result.Data = users
   549  		}
   550  	})
   551  }
   552  
   553  func (us SqlUserStore) GetProfilesWithoutTeam(offset int, limit int) store.StoreChannel {
   554  	return store.Do(func(result *store.StoreResult) {
   555  		var users []*model.User
   556  
   557  		query := `
   558  		SELECT
   559  			*
   560  		FROM
   561  			Users
   562  		WHERE
   563  			(SELECT
   564  				COUNT(0)
   565  			FROM
   566  				TeamMembers
   567  			WHERE
   568  				TeamMembers.UserId = Users.Id
   569  				AND TeamMembers.DeleteAt = 0) = 0
   570  		ORDER BY
   571  			Username ASC
   572  		LIMIT
   573  			:Limit
   574  		OFFSET
   575  			:Offset`
   576  
   577  		if _, err := us.GetReplica().Select(&users, query, map[string]interface{}{"Offset": offset, "Limit": limit}); err != nil {
   578  			result.Err = model.NewAppError("SqlUserStore.GetProfilesWithoutTeam", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   579  		} else {
   580  
   581  			for _, u := range users {
   582  				u.Sanitize(map[string]bool{})
   583  			}
   584  
   585  			result.Data = users
   586  		}
   587  	})
   588  }
   589  
   590  func (us SqlUserStore) GetProfilesByUsernames(usernames []string, teamId string) store.StoreChannel {
   591  	return store.Do(func(result *store.StoreResult) {
   592  		var users []*model.User
   593  		props := make(map[string]interface{})
   594  		idQuery := ""
   595  
   596  		for index, usernames := range usernames {
   597  			if len(idQuery) > 0 {
   598  				idQuery += ", "
   599  			}
   600  
   601  			props["username"+strconv.Itoa(index)] = usernames
   602  			idQuery += ":username" + strconv.Itoa(index)
   603  		}
   604  
   605  		var query string
   606  		if teamId == "" {
   607  			query = `SELECT * FROM Users WHERE Username IN (` + idQuery + `)`
   608  		} else {
   609  			query = `SELECT Users.* FROM Users INNER JOIN TeamMembers ON
   610  				Users.Id = TeamMembers.UserId AND Users.Username IN (` + idQuery + `) AND TeamMembers.TeamId = :TeamId `
   611  			props["TeamId"] = teamId
   612  		}
   613  
   614  		if _, err := us.GetReplica().Select(&users, query, props); err != nil {
   615  			result.Err = model.NewAppError("SqlUserStore.GetProfilesByUsernames", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   616  		} else {
   617  			result.Data = users
   618  		}
   619  	})
   620  }
   621  
   622  type UserWithLastActivityAt struct {
   623  	model.User
   624  	LastActivityAt int64
   625  }
   626  
   627  func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string, offset, limit int) store.StoreChannel {
   628  	return store.Do(func(result *store.StoreResult) {
   629  		var users []*UserWithLastActivityAt
   630  
   631  		if _, err := us.GetReplica().Select(&users, `
   632              SELECT
   633                  u.*,
   634                  s.LastActivityAt
   635              FROM Users AS u
   636                  INNER JOIN TeamMembers AS t ON u.Id = t.UserId
   637                  INNER JOIN Status AS s ON s.UserId = t.UserId
   638              WHERE t.TeamId = :TeamId
   639              ORDER BY s.LastActivityAt DESC
   640              LIMIT :Limit OFFSET :Offset
   641              `, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
   642  			result.Err = model.NewAppError("SqlUserStore.GetRecentlyActiveUsers", "store.sql_user.get_recently_active_users.app_error", nil, err.Error(), http.StatusInternalServerError)
   643  		} else {
   644  
   645  			userList := []*model.User{}
   646  
   647  			for _, userWithLastActivityAt := range users {
   648  				u := userWithLastActivityAt.User
   649  				u.Sanitize(map[string]bool{})
   650  				u.LastActivityAt = userWithLastActivityAt.LastActivityAt
   651  				userList = append(userList, &u)
   652  			}
   653  
   654  			result.Data = userList
   655  		}
   656  	})
   657  }
   658  
   659  func (us SqlUserStore) GetNewUsersForTeam(teamId string, offset, limit int) store.StoreChannel {
   660  	return store.Do(func(result *store.StoreResult) {
   661  		var users []*model.User
   662  
   663  		if _, err := us.GetReplica().Select(&users, `
   664              SELECT
   665                  u.*
   666              FROM Users AS u
   667                  INNER JOIN TeamMembers AS t ON u.Id = t.UserId
   668              WHERE t.TeamId = :TeamId
   669              ORDER BY u.CreateAt DESC
   670              LIMIT :Limit OFFSET :Offset
   671              `, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
   672  			result.Err = model.NewAppError("SqlUserStore.GetNewUsersForTeam", "store.sql_user.get_new_users.app_error", nil, err.Error(), http.StatusInternalServerError)
   673  		} else {
   674  			for _, u := range users {
   675  				u.Sanitize(map[string]bool{})
   676  			}
   677  
   678  			result.Data = users
   679  		}
   680  	})
   681  }
   682  
   683  func (us SqlUserStore) GetProfileByIds(userIds []string, allowFromCache bool) store.StoreChannel {
   684  	return store.Do(func(result *store.StoreResult) {
   685  		users := []*model.User{}
   686  		props := make(map[string]interface{})
   687  		idQuery := ""
   688  		remainingUserIds := make([]string, 0)
   689  
   690  		if allowFromCache {
   691  			for _, userId := range userIds {
   692  				if cacheItem, ok := profileByIdsCache.Get(userId); ok {
   693  					u := &model.User{}
   694  					*u = *cacheItem.(*model.User)
   695  					users = append(users, u)
   696  				} else {
   697  					remainingUserIds = append(remainingUserIds, userId)
   698  				}
   699  			}
   700  			if us.metrics != nil {
   701  				us.metrics.AddMemCacheHitCounter("Profile By Ids", float64(len(users)))
   702  				us.metrics.AddMemCacheMissCounter("Profile By Ids", float64(len(remainingUserIds)))
   703  			}
   704  		} else {
   705  			remainingUserIds = userIds
   706  			if us.metrics != nil {
   707  				us.metrics.AddMemCacheMissCounter("Profile By Ids", float64(len(remainingUserIds)))
   708  			}
   709  		}
   710  
   711  		// If everything came from the cache then just return
   712  		if len(remainingUserIds) == 0 {
   713  			result.Data = users
   714  			return
   715  		}
   716  
   717  		for index, userId := range remainingUserIds {
   718  			if len(idQuery) > 0 {
   719  				idQuery += ", "
   720  			}
   721  
   722  			props["userId"+strconv.Itoa(index)] = userId
   723  			idQuery += ":userId" + strconv.Itoa(index)
   724  		}
   725  
   726  		if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Users.Id IN ("+idQuery+")", props); err != nil {
   727  			result.Err = model.NewAppError("SqlUserStore.GetProfileByIds", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   728  		} else {
   729  
   730  			for _, u := range users {
   731  				u.Sanitize(map[string]bool{})
   732  
   733  				cpy := &model.User{}
   734  				*cpy = *u
   735  				profileByIdsCache.AddWithExpiresInSecs(cpy.Id, cpy, PROFILE_BY_IDS_CACHE_SEC)
   736  			}
   737  
   738  			result.Data = users
   739  		}
   740  	})
   741  }
   742  
   743  func (us SqlUserStore) GetSystemAdminProfiles() store.StoreChannel {
   744  	return store.Do(func(result *store.StoreResult) {
   745  		var users []*model.User
   746  
   747  		if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Roles LIKE :Roles", map[string]interface{}{"Roles": "%system_admin%"}); err != nil {
   748  			result.Err = model.NewAppError("SqlUserStore.GetSystemAdminProfiles", "store.sql_user.get_sysadmin_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
   749  		} else {
   750  
   751  			userMap := make(map[string]*model.User)
   752  
   753  			for _, u := range users {
   754  				u.Sanitize(map[string]bool{})
   755  				userMap[u.Id] = u
   756  			}
   757  
   758  			result.Data = userMap
   759  		}
   760  	})
   761  }
   762  
   763  func (us SqlUserStore) GetByEmail(email string) store.StoreChannel {
   764  	return store.Do(func(result *store.StoreResult) {
   765  		email = strings.ToLower(email)
   766  
   767  		user := model.User{}
   768  
   769  		if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE Email = :Email", map[string]interface{}{"Email": email}); err != nil {
   770  			result.Err = model.NewAppError("SqlUserStore.GetByEmail", store.MISSING_ACCOUNT_ERROR, nil, "email="+email+", "+err.Error(), http.StatusInternalServerError)
   771  		}
   772  
   773  		result.Data = &user
   774  	})
   775  }
   776  
   777  func (us SqlUserStore) GetByAuth(authData *string, authService string) store.StoreChannel {
   778  	return store.Do(func(result *store.StoreResult) {
   779  		if authData == nil || *authData == "" {
   780  			result.Err = model.NewAppError("SqlUserStore.GetByAuth", store.MISSING_AUTH_ACCOUNT_ERROR, nil, "authData='', authService="+authService, http.StatusBadRequest)
   781  			return
   782  		}
   783  
   784  		user := model.User{}
   785  
   786  		if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE AuthData = :AuthData AND AuthService = :AuthService", map[string]interface{}{"AuthData": authData, "AuthService": authService}); err != nil {
   787  			if err == sql.ErrNoRows {
   788  				result.Err = model.NewAppError("SqlUserStore.GetByAuth", store.MISSING_AUTH_ACCOUNT_ERROR, nil, "authData="+*authData+", authService="+authService+", "+err.Error(), http.StatusInternalServerError)
   789  			} else {
   790  				result.Err = model.NewAppError("SqlUserStore.GetByAuth", "store.sql_user.get_by_auth.other.app_error", nil, "authData="+*authData+", authService="+authService+", "+err.Error(), http.StatusInternalServerError)
   791  			}
   792  		}
   793  
   794  		result.Data = &user
   795  	})
   796  }
   797  
   798  func (us SqlUserStore) GetAllUsingAuthService(authService string) store.StoreChannel {
   799  	return store.Do(func(result *store.StoreResult) {
   800  		var data []*model.User
   801  
   802  		if _, err := us.GetReplica().Select(&data, "SELECT * FROM Users WHERE AuthService = :AuthService", map[string]interface{}{"AuthService": authService}); err != nil {
   803  			result.Err = model.NewAppError("SqlUserStore.GetByAuth", "store.sql_user.get_by_auth.other.app_error", nil, "authService="+authService+", "+err.Error(), http.StatusInternalServerError)
   804  		}
   805  
   806  		result.Data = data
   807  	})
   808  }
   809  
   810  func (us SqlUserStore) GetByUsername(username string) store.StoreChannel {
   811  	return store.Do(func(result *store.StoreResult) {
   812  		user := model.User{}
   813  
   814  		if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE Username = :Username", map[string]interface{}{"Username": username}); err != nil {
   815  			result.Err = model.NewAppError("SqlUserStore.GetByUsername", "store.sql_user.get_by_username.app_error", nil, err.Error(), http.StatusInternalServerError)
   816  		}
   817  
   818  		result.Data = &user
   819  	})
   820  }
   821  
   822  func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) store.StoreChannel {
   823  	return store.Do(func(result *store.StoreResult) {
   824  		params := map[string]interface{}{
   825  			"LoginId":                 loginId,
   826  			"AllowSignInWithUsername": allowSignInWithUsername,
   827  			"AllowSignInWithEmail":    allowSignInWithEmail,
   828  			"LdapEnabled":             ldapEnabled,
   829  		}
   830  
   831  		users := []*model.User{}
   832  		if _, err := us.GetReplica().Select(
   833  			&users,
   834  			`SELECT
   835  				*
   836  			FROM
   837  				Users
   838  			WHERE
   839  				(:AllowSignInWithUsername AND Username = :LoginId)
   840  				OR (:AllowSignInWithEmail AND Email = :LoginId)
   841  				OR (:LdapEnabled AND AuthService = '`+model.USER_AUTH_SERVICE_LDAP+`' AND AuthData = :LoginId)`,
   842  			params); err != nil {
   843  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error(), http.StatusInternalServerError)
   844  		} else if len(users) == 1 {
   845  			result.Data = users[0]
   846  		} else if len(users) > 1 {
   847  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.multiple_users", nil, "", http.StatusInternalServerError)
   848  		} else {
   849  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusInternalServerError)
   850  		}
   851  	})
   852  }
   853  
   854  func (us SqlUserStore) VerifyEmail(userId string) store.StoreChannel {
   855  	return store.Do(func(result *store.StoreResult) {
   856  		if _, err := us.GetMaster().Exec("UPDATE Users SET EmailVerified = true WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
   857  			result.Err = model.NewAppError("SqlUserStore.VerifyEmail", "store.sql_user.verify_email.app_error", nil, "userId="+userId+", "+err.Error(), http.StatusInternalServerError)
   858  		}
   859  
   860  		result.Data = userId
   861  	})
   862  }
   863  
   864  func (us SqlUserStore) GetTotalUsersCount() store.StoreChannel {
   865  	return store.Do(func(result *store.StoreResult) {
   866  		if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users"); err != nil {
   867  			result.Err = model.NewAppError("SqlUserStore.GetTotalUsersCount", "store.sql_user.get_total_users_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   868  		} else {
   869  			result.Data = count
   870  		}
   871  	})
   872  }
   873  
   874  func (us SqlUserStore) PermanentDelete(userId string) store.StoreChannel {
   875  	return store.Do(func(result *store.StoreResult) {
   876  		if _, err := us.GetMaster().Exec("DELETE FROM Users WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
   877  			result.Err = model.NewAppError("SqlUserStore.PermanentDelete", "store.sql_user.permanent_delete.app_error", nil, "userId="+userId+", "+err.Error(), http.StatusInternalServerError)
   878  		}
   879  	})
   880  }
   881  
   882  func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) store.StoreChannel {
   883  	return store.Do(func(result *store.StoreResult) {
   884  		query := ""
   885  		if len(teamId) > 0 {
   886  			query = "SELECT COUNT(DISTINCT Users.Email) From Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId AND TeamMembers.DeleteAt = 0 AND Users.DeleteAt = 0"
   887  		} else {
   888  			query = "SELECT COUNT(DISTINCT Email) FROM Users WHERE DeleteAt = 0"
   889  		}
   890  
   891  		v, err := us.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId})
   892  		if err != nil {
   893  			result.Err = model.NewAppError("SqlUserStore.AnalyticsUniqueUserCount", "store.sql_user.analytics_unique_user_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   894  		} else {
   895  			result.Data = v
   896  		}
   897  	})
   898  }
   899  
   900  func (us SqlUserStore) AnalyticsActiveCount(timePeriod int64) store.StoreChannel {
   901  	return store.Do(func(result *store.StoreResult) {
   902  		time := model.GetMillis() - timePeriod
   903  
   904  		query := "SELECT COUNT(*) FROM Status WHERE LastActivityAt > :Time"
   905  
   906  		v, err := us.GetReplica().SelectInt(query, map[string]interface{}{"Time": time})
   907  		if err != nil {
   908  			result.Err = model.NewAppError("SqlUserStore.AnalyticsDailyActiveUsers", "store.sql_user.analytics_daily_active_users.app_error", nil, err.Error(), http.StatusInternalServerError)
   909  		} else {
   910  			result.Data = v
   911  		}
   912  	})
   913  }
   914  
   915  func (us SqlUserStore) GetUnreadCount(userId string) store.StoreChannel {
   916  	return store.Do(func(result *store.StoreResult) {
   917  		if count, err := us.GetReplica().SelectInt(`
   918  		SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END)
   919  		FROM Channels c
   920  		INNER JOIN ChannelMembers cm
   921  		      ON cm.ChannelId = c.Id
   922  		      AND cm.UserId = :UserId
   923  		      AND c.DeleteAt = 0`, map[string]interface{}{"UserId": userId}); err != nil {
   924  			result.Err = model.NewAppError("SqlUserStore.GetMentionCount", "store.sql_user.get_unread_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   925  		} else {
   926  			result.Data = count
   927  		}
   928  	})
   929  }
   930  
   931  func (us SqlUserStore) GetUnreadCountForChannel(userId string, channelId string) store.StoreChannel {
   932  	return store.Do(func(result *store.StoreResult) {
   933  		if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END) FROM Channels c INNER JOIN ChannelMembers cm ON c.Id = :ChannelId AND cm.ChannelId = :ChannelId AND cm.UserId = :UserId", map[string]interface{}{"ChannelId": channelId, "UserId": userId}); err != nil {
   934  			result.Err = model.NewAppError("SqlUserStore.GetMentionCountForChannel", "store.sql_user.get_unread_count_for_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
   935  		} else {
   936  			result.Data = count
   937  		}
   938  	})
   939  }
   940  
   941  func (us SqlUserStore) Search(teamId string, term string, options map[string]bool) store.StoreChannel {
   942  	return store.Do(func(result *store.StoreResult) {
   943  		searchQuery := ""
   944  
   945  		if teamId == "" {
   946  
   947  			// Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
   948  			searchQuery = `
   949  			SELECT
   950  				*
   951  			FROM
   952  				Users
   953  			WHERE
   954  				Id != ''
   955  				SEARCH_CLAUSE
   956  				INACTIVE_CLAUSE
   957  				ORDER BY Username ASC
   958  			LIMIT 100`
   959  		} else {
   960  			searchQuery = `
   961  			SELECT
   962  				Users.*
   963  			FROM
   964  				Users, TeamMembers
   965  			WHERE
   966  				TeamMembers.TeamId = :TeamId
   967  				AND Users.Id = TeamMembers.UserId
   968  				AND TeamMembers.DeleteAt = 0
   969  				SEARCH_CLAUSE
   970  				INACTIVE_CLAUSE
   971  				ORDER BY Users.Username ASC
   972  			LIMIT 100`
   973  		}
   974  
   975  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId})
   976  
   977  	})
   978  }
   979  
   980  func (us SqlUserStore) SearchWithoutTeam(term string, options map[string]bool) store.StoreChannel {
   981  	return store.Do(func(result *store.StoreResult) {
   982  		searchQuery := `
   983  		SELECT
   984  			*
   985  		FROM
   986  			Users
   987  		WHERE
   988  			(SELECT
   989  				COUNT(0)
   990  			FROM
   991  				TeamMembers
   992  			WHERE
   993  				TeamMembers.UserId = Users.Id
   994  				AND TeamMembers.DeleteAt = 0) = 0
   995  			SEARCH_CLAUSE
   996  			INACTIVE_CLAUSE
   997  			ORDER BY Username ASC
   998  		LIMIT 100`
   999  
  1000  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{})
  1001  
  1002  	})
  1003  }
  1004  
  1005  func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options map[string]bool) store.StoreChannel {
  1006  	return store.Do(func(result *store.StoreResult) {
  1007  		searchQuery := `
  1008  			SELECT
  1009  				Users.*
  1010  			FROM Users
  1011  			LEFT JOIN TeamMembers tm
  1012  				ON tm.UserId = Users.Id
  1013  				AND tm.TeamId = :NotInTeamId
  1014  			WHERE
  1015  				(tm.UserId IS NULL OR tm.DeleteAt != 0)
  1016  				SEARCH_CLAUSE
  1017  				INACTIVE_CLAUSE
  1018  			ORDER BY Users.Username ASC
  1019  			LIMIT 100`
  1020  
  1021  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"NotInTeamId": notInTeamId})
  1022  
  1023  	})
  1024  }
  1025  
  1026  func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) store.StoreChannel {
  1027  	return store.Do(func(result *store.StoreResult) {
  1028  		searchQuery := ""
  1029  		if teamId == "" {
  1030  			searchQuery = `
  1031  			SELECT
  1032  				Users.*
  1033  			FROM Users
  1034  			LEFT JOIN ChannelMembers cm
  1035  				ON cm.UserId = Users.Id
  1036  				AND cm.ChannelId = :ChannelId
  1037  			WHERE
  1038  				cm.UserId IS NULL
  1039  				SEARCH_CLAUSE
  1040  				INACTIVE_CLAUSE
  1041  			ORDER BY Users.Username ASC
  1042  			LIMIT 100`
  1043  		} else {
  1044  			searchQuery = `
  1045  			SELECT
  1046  				Users.*
  1047  			FROM Users
  1048  			INNER JOIN TeamMembers tm
  1049  				ON tm.UserId = Users.Id
  1050  				AND tm.TeamId = :TeamId
  1051  				AND tm.DeleteAt = 0
  1052  			LEFT JOIN ChannelMembers cm
  1053  				ON cm.UserId = Users.Id
  1054  				AND cm.ChannelId = :ChannelId
  1055  			WHERE
  1056  				cm.UserId IS NULL
  1057  				SEARCH_CLAUSE
  1058  				INACTIVE_CLAUSE
  1059  			ORDER BY Users.Username ASC
  1060  			LIMIT 100`
  1061  		}
  1062  
  1063  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId})
  1064  
  1065  	})
  1066  }
  1067  
  1068  func (us SqlUserStore) SearchInChannel(channelId string, term string, options map[string]bool) store.StoreChannel {
  1069  	return store.Do(func(result *store.StoreResult) {
  1070  		searchQuery := `
  1071          SELECT
  1072              Users.*
  1073          FROM
  1074              Users, ChannelMembers
  1075          WHERE
  1076              ChannelMembers.ChannelId = :ChannelId
  1077              AND ChannelMembers.UserId = Users.Id
  1078              SEARCH_CLAUSE
  1079              INACTIVE_CLAUSE
  1080              ORDER BY Users.Username ASC
  1081          LIMIT 100`
  1082  
  1083  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"ChannelId": channelId})
  1084  
  1085  	})
  1086  }
  1087  
  1088  var escapeLikeSearchChar = []string{
  1089  	"%",
  1090  	"_",
  1091  }
  1092  
  1093  var ignoreLikeSearchChar = []string{
  1094  	"*",
  1095  }
  1096  
  1097  var spaceFulltextSearchChar = []string{
  1098  	"<",
  1099  	">",
  1100  	"+",
  1101  	"-",
  1102  	"(",
  1103  	")",
  1104  	"~",
  1105  	":",
  1106  	"*",
  1107  	"\"",
  1108  	"!",
  1109  	"@",
  1110  }
  1111  
  1112  func generateSearchQuery(searchQuery string, terms []string, fields []string, parameters map[string]interface{}, isPostgreSQL bool) string {
  1113  	searchTerms := []string{}
  1114  	for i, term := range terms {
  1115  		searchFields := []string{}
  1116  		for _, field := range fields {
  1117  			if isPostgreSQL {
  1118  				searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*' ", field, fmt.Sprintf(":Term%d", i)))
  1119  			} else {
  1120  				searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*' ", field, fmt.Sprintf(":Term%d", i)))
  1121  			}
  1122  		}
  1123  		searchTerms = append(searchTerms, fmt.Sprintf("(%s)", strings.Join(searchFields, " OR ")))
  1124  		parameters[fmt.Sprintf("Term%d", i)] = fmt.Sprintf("%s%%", term)
  1125  	}
  1126  
  1127  	searchClause := strings.Join(searchTerms, " AND ")
  1128  	return strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", searchClause), 1)
  1129  }
  1130  
  1131  func (us SqlUserStore) performSearch(searchQuery string, term string, options map[string]bool, parameters map[string]interface{}) store.StoreResult {
  1132  	result := store.StoreResult{}
  1133  
  1134  	// These chars must be removed from the like query.
  1135  	for _, c := range ignoreLikeSearchChar {
  1136  		term = strings.Replace(term, c, "", -1)
  1137  	}
  1138  
  1139  	// These chars must be escaped in the like query.
  1140  	for _, c := range escapeLikeSearchChar {
  1141  		term = strings.Replace(term, c, "*"+c, -1)
  1142  	}
  1143  
  1144  	searchType := USER_SEARCH_TYPE_ALL
  1145  	if ok := options[store.USER_SEARCH_OPTION_NAMES_ONLY]; ok {
  1146  		searchType = USER_SEARCH_TYPE_NAMES
  1147  	} else if ok = options[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME]; ok {
  1148  		searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
  1149  	} else if ok = options[store.USER_SEARCH_OPTION_ALL_NO_FULL_NAME]; ok {
  1150  		searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
  1151  	}
  1152  
  1153  	if ok := options[store.USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
  1154  		searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "", 1)
  1155  	} else {
  1156  		searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "AND Users.DeleteAt = 0", 1)
  1157  	}
  1158  
  1159  	if strings.TrimSpace(term) == "" {
  1160  		searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
  1161  	} else {
  1162  		isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
  1163  		searchQuery = generateSearchQuery(searchQuery, strings.Fields(term), searchType, parameters, isPostgreSQL)
  1164  	}
  1165  
  1166  	var users []*model.User
  1167  
  1168  	if _, err := us.GetReplica().Select(&users, searchQuery, parameters); err != nil {
  1169  		result.Err = model.NewAppError("SqlUserStore.Search", "store.sql_user.search.app_error", nil,
  1170  			fmt.Sprintf("term=%v, search_type=%v, %v", term, searchType, err.Error()), http.StatusInternalServerError)
  1171  	} else {
  1172  		for _, u := range users {
  1173  			u.Sanitize(map[string]bool{})
  1174  		}
  1175  
  1176  		result.Data = users
  1177  	}
  1178  
  1179  	return result
  1180  }
  1181  
  1182  func (us SqlUserStore) AnalyticsGetInactiveUsersCount() store.StoreChannel {
  1183  	return store.Do(func(result *store.StoreResult) {
  1184  		if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users WHERE DeleteAt > 0"); err != nil {
  1185  			result.Err = model.NewAppError("SqlUserStore.AnalyticsGetInactiveUsersCount", "store.sql_user.analytics_get_inactive_users_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  1186  		} else {
  1187  			result.Data = count
  1188  		}
  1189  	})
  1190  }
  1191  
  1192  func (us SqlUserStore) AnalyticsGetSystemAdminCount() store.StoreChannel {
  1193  	return store.Do(func(result *store.StoreResult) {
  1194  		if count, err := us.GetReplica().SelectInt("SELECT count(*) FROM Users WHERE Roles LIKE :Roles and DeleteAt = 0", map[string]interface{}{"Roles": "%system_admin%"}); err != nil {
  1195  			result.Err = model.NewAppError("SqlUserStore.AnalyticsGetSystemAdminCount", "store.sql_user.analytics_get_system_admin_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  1196  		} else {
  1197  			result.Data = count
  1198  		}
  1199  	})
  1200  }
  1201  
  1202  func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int) store.StoreChannel {
  1203  	return store.Do(func(result *store.StoreResult) {
  1204  		var users []*model.User
  1205  
  1206  		if _, err := us.GetReplica().Select(&users, `
  1207              SELECT
  1208                  u.*
  1209              FROM Users u
  1210              LEFT JOIN TeamMembers tm
  1211                  ON tm.UserId = u.Id
  1212                  AND tm.TeamId = :TeamId
  1213                  AND tm.DeleteAt = 0
  1214              WHERE tm.UserId IS NULL
  1215              ORDER BY u.Username ASC
  1216              LIMIT :Limit OFFSET :Offset
  1217              `, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
  1218  			result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInTeam", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
  1219  		} else {
  1220  
  1221  			for _, u := range users {
  1222  				u.Sanitize(map[string]bool{})
  1223  			}
  1224  
  1225  			result.Data = users
  1226  		}
  1227  	})
  1228  }
  1229  
  1230  func (us SqlUserStore) GetEtagForProfilesNotInTeam(teamId string) store.StoreChannel {
  1231  	return store.Do(func(result *store.StoreResult) {
  1232  		updateAt, err := us.GetReplica().SelectInt(`
  1233              SELECT
  1234                  u.UpdateAt
  1235              FROM Users u
  1236              LEFT JOIN TeamMembers tm
  1237                  ON tm.UserId = u.Id
  1238                  AND tm.TeamId = :TeamId
  1239                  AND tm.DeleteAt = 0
  1240              WHERE tm.UserId IS NULL
  1241              ORDER BY u.UpdateAt DESC
  1242              LIMIT 1
  1243              `, map[string]interface{}{"TeamId": teamId})
  1244  
  1245  		if err != nil {
  1246  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
  1247  		} else {
  1248  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt)
  1249  		}
  1250  	})
  1251  }