github.com/qichengzx/mattermost-server@v4.5.1-0.20180604164826-2c75247c97d0+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 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  		}
   829  
   830  		users := []*model.User{}
   831  		if _, err := us.GetReplica().Select(
   832  			&users,
   833  			`SELECT
   834  				*
   835  			FROM
   836  				Users
   837  			WHERE
   838  				(:AllowSignInWithUsername AND Username = :LoginId)
   839  				OR (:AllowSignInWithEmail AND Email = :LoginId)`,
   840  			params); err != nil {
   841  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error(), http.StatusInternalServerError)
   842  		} else if len(users) == 1 {
   843  			result.Data = users[0]
   844  		} else if len(users) > 1 {
   845  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.multiple_users", nil, "", http.StatusInternalServerError)
   846  		} else {
   847  			result.Err = model.NewAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, "", http.StatusInternalServerError)
   848  		}
   849  	})
   850  }
   851  
   852  func (us SqlUserStore) VerifyEmail(userId string) store.StoreChannel {
   853  	return store.Do(func(result *store.StoreResult) {
   854  		if _, err := us.GetMaster().Exec("UPDATE Users SET EmailVerified = true WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
   855  			result.Err = model.NewAppError("SqlUserStore.VerifyEmail", "store.sql_user.verify_email.app_error", nil, "userId="+userId+", "+err.Error(), http.StatusInternalServerError)
   856  		}
   857  
   858  		result.Data = userId
   859  	})
   860  }
   861  
   862  func (us SqlUserStore) GetTotalUsersCount() store.StoreChannel {
   863  	return store.Do(func(result *store.StoreResult) {
   864  		if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users"); err != nil {
   865  			result.Err = model.NewAppError("SqlUserStore.GetTotalUsersCount", "store.sql_user.get_total_users_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   866  		} else {
   867  			result.Data = count
   868  		}
   869  	})
   870  }
   871  
   872  func (us SqlUserStore) PermanentDelete(userId string) store.StoreChannel {
   873  	return store.Do(func(result *store.StoreResult) {
   874  		if _, err := us.GetMaster().Exec("DELETE FROM Users WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil {
   875  			result.Err = model.NewAppError("SqlUserStore.PermanentDelete", "store.sql_user.permanent_delete.app_error", nil, "userId="+userId+", "+err.Error(), http.StatusInternalServerError)
   876  		}
   877  	})
   878  }
   879  
   880  func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) store.StoreChannel {
   881  	return store.Do(func(result *store.StoreResult) {
   882  		query := ""
   883  		if len(teamId) > 0 {
   884  			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"
   885  		} else {
   886  			query = "SELECT COUNT(DISTINCT Email) FROM Users WHERE DeleteAt = 0"
   887  		}
   888  
   889  		v, err := us.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId})
   890  		if err != nil {
   891  			result.Err = model.NewAppError("SqlUserStore.AnalyticsUniqueUserCount", "store.sql_user.analytics_unique_user_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   892  		} else {
   893  			result.Data = v
   894  		}
   895  	})
   896  }
   897  
   898  func (us SqlUserStore) AnalyticsActiveCount(timePeriod int64) store.StoreChannel {
   899  	return store.Do(func(result *store.StoreResult) {
   900  		time := model.GetMillis() - timePeriod
   901  
   902  		query := "SELECT COUNT(*) FROM Status WHERE LastActivityAt > :Time"
   903  
   904  		v, err := us.GetReplica().SelectInt(query, map[string]interface{}{"Time": time})
   905  		if err != nil {
   906  			result.Err = model.NewAppError("SqlUserStore.AnalyticsDailyActiveUsers", "store.sql_user.analytics_daily_active_users.app_error", nil, err.Error(), http.StatusInternalServerError)
   907  		} else {
   908  			result.Data = v
   909  		}
   910  	})
   911  }
   912  
   913  func (us SqlUserStore) GetUnreadCount(userId string) store.StoreChannel {
   914  	return store.Do(func(result *store.StoreResult) {
   915  		if count, err := us.GetReplica().SelectInt(`
   916  		SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE cm.MentionCount END)
   917  		FROM Channels c
   918  		INNER JOIN ChannelMembers cm
   919  		      ON cm.ChannelId = c.Id
   920  		      AND cm.UserId = :UserId
   921  		      AND c.DeleteAt = 0`, map[string]interface{}{"UserId": userId}); err != nil {
   922  			result.Err = model.NewAppError("SqlUserStore.GetMentionCount", "store.sql_user.get_unread_count.app_error", nil, err.Error(), http.StatusInternalServerError)
   923  		} else {
   924  			result.Data = count
   925  		}
   926  	})
   927  }
   928  
   929  func (us SqlUserStore) GetUnreadCountForChannel(userId string, channelId string) store.StoreChannel {
   930  	return store.Do(func(result *store.StoreResult) {
   931  		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 {
   932  			result.Err = model.NewAppError("SqlUserStore.GetMentionCountForChannel", "store.sql_user.get_unread_count_for_channel.app_error", nil, err.Error(), http.StatusInternalServerError)
   933  		} else {
   934  			result.Data = count
   935  		}
   936  	})
   937  }
   938  
   939  func (us SqlUserStore) Search(teamId string, term string, options map[string]bool) store.StoreChannel {
   940  	return store.Do(func(result *store.StoreResult) {
   941  		searchQuery := ""
   942  
   943  		if teamId == "" {
   944  
   945  			// Id != '' is added because both SEARCH_CLAUSE and INACTIVE_CLAUSE start with an AND
   946  			searchQuery = `
   947  			SELECT
   948  				*
   949  			FROM
   950  				Users
   951  			WHERE
   952  				Id != ''
   953  				SEARCH_CLAUSE
   954  				INACTIVE_CLAUSE
   955  				ORDER BY Username ASC
   956  			LIMIT 100`
   957  		} else {
   958  			searchQuery = `
   959  			SELECT
   960  				Users.*
   961  			FROM
   962  				Users, TeamMembers
   963  			WHERE
   964  				TeamMembers.TeamId = :TeamId
   965  				AND Users.Id = TeamMembers.UserId
   966  				AND TeamMembers.DeleteAt = 0
   967  				SEARCH_CLAUSE
   968  				INACTIVE_CLAUSE
   969  				ORDER BY Users.Username ASC
   970  			LIMIT 100`
   971  		}
   972  
   973  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId})
   974  
   975  	})
   976  }
   977  
   978  func (us SqlUserStore) SearchWithoutTeam(term string, options map[string]bool) store.StoreChannel {
   979  	return store.Do(func(result *store.StoreResult) {
   980  		searchQuery := `
   981  		SELECT
   982  			*
   983  		FROM
   984  			Users
   985  		WHERE
   986  			(SELECT
   987  				COUNT(0)
   988  			FROM
   989  				TeamMembers
   990  			WHERE
   991  				TeamMembers.UserId = Users.Id
   992  				AND TeamMembers.DeleteAt = 0) = 0
   993  			SEARCH_CLAUSE
   994  			INACTIVE_CLAUSE
   995  			ORDER BY Username ASC
   996  		LIMIT 100`
   997  
   998  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{})
   999  
  1000  	})
  1001  }
  1002  
  1003  func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options map[string]bool) store.StoreChannel {
  1004  	return store.Do(func(result *store.StoreResult) {
  1005  		searchQuery := `
  1006  			SELECT
  1007  				Users.*
  1008  			FROM Users
  1009  			LEFT JOIN TeamMembers tm
  1010  				ON tm.UserId = Users.Id
  1011  				AND tm.TeamId = :NotInTeamId
  1012  			WHERE
  1013  				(tm.UserId IS NULL OR tm.DeleteAt != 0)
  1014  				SEARCH_CLAUSE
  1015  				INACTIVE_CLAUSE
  1016  			ORDER BY Users.Username ASC
  1017  			LIMIT 100`
  1018  
  1019  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"NotInTeamId": notInTeamId})
  1020  
  1021  	})
  1022  }
  1023  
  1024  func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) store.StoreChannel {
  1025  	return store.Do(func(result *store.StoreResult) {
  1026  		searchQuery := ""
  1027  		if teamId == "" {
  1028  			searchQuery = `
  1029  			SELECT
  1030  				Users.*
  1031  			FROM Users
  1032  			LEFT JOIN ChannelMembers cm
  1033  				ON cm.UserId = Users.Id
  1034  				AND cm.ChannelId = :ChannelId
  1035  			WHERE
  1036  				cm.UserId IS NULL
  1037  				SEARCH_CLAUSE
  1038  				INACTIVE_CLAUSE
  1039  			ORDER BY Users.Username ASC
  1040  			LIMIT 100`
  1041  		} else {
  1042  			searchQuery = `
  1043  			SELECT
  1044  				Users.*
  1045  			FROM Users
  1046  			INNER JOIN TeamMembers tm
  1047  				ON tm.UserId = Users.Id
  1048  				AND tm.TeamId = :TeamId
  1049  				AND tm.DeleteAt = 0
  1050  			LEFT JOIN ChannelMembers cm
  1051  				ON cm.UserId = Users.Id
  1052  				AND cm.ChannelId = :ChannelId
  1053  			WHERE
  1054  				cm.UserId IS NULL
  1055  				SEARCH_CLAUSE
  1056  				INACTIVE_CLAUSE
  1057  			ORDER BY Users.Username ASC
  1058  			LIMIT 100`
  1059  		}
  1060  
  1061  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"TeamId": teamId, "ChannelId": channelId})
  1062  
  1063  	})
  1064  }
  1065  
  1066  func (us SqlUserStore) SearchInChannel(channelId string, term string, options map[string]bool) store.StoreChannel {
  1067  	return store.Do(func(result *store.StoreResult) {
  1068  		searchQuery := `
  1069          SELECT
  1070              Users.*
  1071          FROM
  1072              Users, ChannelMembers
  1073          WHERE
  1074              ChannelMembers.ChannelId = :ChannelId
  1075              AND ChannelMembers.UserId = Users.Id
  1076              SEARCH_CLAUSE
  1077              INACTIVE_CLAUSE
  1078              ORDER BY Users.Username ASC
  1079          LIMIT 100`
  1080  
  1081  		*result = us.performSearch(searchQuery, term, options, map[string]interface{}{"ChannelId": channelId})
  1082  
  1083  	})
  1084  }
  1085  
  1086  var escapeLikeSearchChar = []string{
  1087  	"%",
  1088  	"_",
  1089  }
  1090  
  1091  var ignoreLikeSearchChar = []string{
  1092  	"*",
  1093  }
  1094  
  1095  var spaceFulltextSearchChar = []string{
  1096  	"<",
  1097  	">",
  1098  	"+",
  1099  	"-",
  1100  	"(",
  1101  	")",
  1102  	"~",
  1103  	":",
  1104  	"*",
  1105  	"\"",
  1106  	"!",
  1107  	"@",
  1108  }
  1109  
  1110  func generateSearchQuery(searchQuery string, terms []string, fields []string, parameters map[string]interface{}, isPostgreSQL bool) string {
  1111  	searchTerms := []string{}
  1112  	for i, term := range terms {
  1113  		searchFields := []string{}
  1114  		for _, field := range fields {
  1115  			if isPostgreSQL {
  1116  				searchFields = append(searchFields, fmt.Sprintf("lower(%s) LIKE lower(%s) escape '*' ", field, fmt.Sprintf(":Term%d", i)))
  1117  			} else {
  1118  				searchFields = append(searchFields, fmt.Sprintf("%s LIKE %s escape '*' ", field, fmt.Sprintf(":Term%d", i)))
  1119  			}
  1120  		}
  1121  		searchTerms = append(searchTerms, fmt.Sprintf("(%s)", strings.Join(searchFields, " OR ")))
  1122  		parameters[fmt.Sprintf("Term%d", i)] = fmt.Sprintf("%s%%", term)
  1123  	}
  1124  
  1125  	searchClause := strings.Join(searchTerms, " AND ")
  1126  	return strings.Replace(searchQuery, "SEARCH_CLAUSE", fmt.Sprintf(" AND %s ", searchClause), 1)
  1127  }
  1128  
  1129  func (us SqlUserStore) performSearch(searchQuery string, term string, options map[string]bool, parameters map[string]interface{}) store.StoreResult {
  1130  	result := store.StoreResult{}
  1131  
  1132  	// These chars must be removed from the like query.
  1133  	for _, c := range ignoreLikeSearchChar {
  1134  		term = strings.Replace(term, c, "", -1)
  1135  	}
  1136  
  1137  	// These chars must be escaped in the like query.
  1138  	for _, c := range escapeLikeSearchChar {
  1139  		term = strings.Replace(term, c, "*"+c, -1)
  1140  	}
  1141  
  1142  	searchType := USER_SEARCH_TYPE_ALL
  1143  	if ok := options[store.USER_SEARCH_OPTION_NAMES_ONLY]; ok {
  1144  		searchType = USER_SEARCH_TYPE_NAMES
  1145  	} else if ok = options[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME]; ok {
  1146  		searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
  1147  	} else if ok = options[store.USER_SEARCH_OPTION_ALL_NO_FULL_NAME]; ok {
  1148  		searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
  1149  	}
  1150  
  1151  	if ok := options[store.USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
  1152  		searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "", 1)
  1153  	} else {
  1154  		searchQuery = strings.Replace(searchQuery, "INACTIVE_CLAUSE", "AND Users.DeleteAt = 0", 1)
  1155  	}
  1156  
  1157  	if strings.TrimSpace(term) == "" {
  1158  		searchQuery = strings.Replace(searchQuery, "SEARCH_CLAUSE", "", 1)
  1159  	} else {
  1160  		isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
  1161  		searchQuery = generateSearchQuery(searchQuery, strings.Fields(term), searchType, parameters, isPostgreSQL)
  1162  	}
  1163  
  1164  	var users []*model.User
  1165  
  1166  	if _, err := us.GetReplica().Select(&users, searchQuery, parameters); err != nil {
  1167  		result.Err = model.NewAppError("SqlUserStore.Search", "store.sql_user.search.app_error", nil,
  1168  			fmt.Sprintf("term=%v, search_type=%v, %v", term, searchType, err.Error()), http.StatusInternalServerError)
  1169  	} else {
  1170  		for _, u := range users {
  1171  			u.Sanitize(map[string]bool{})
  1172  		}
  1173  
  1174  		result.Data = users
  1175  	}
  1176  
  1177  	return result
  1178  }
  1179  
  1180  func (us SqlUserStore) AnalyticsGetInactiveUsersCount() store.StoreChannel {
  1181  	return store.Do(func(result *store.StoreResult) {
  1182  		if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users WHERE DeleteAt > 0"); err != nil {
  1183  			result.Err = model.NewAppError("SqlUserStore.AnalyticsGetInactiveUsersCount", "store.sql_user.analytics_get_inactive_users_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  1184  		} else {
  1185  			result.Data = count
  1186  		}
  1187  	})
  1188  }
  1189  
  1190  func (us SqlUserStore) AnalyticsGetSystemAdminCount() store.StoreChannel {
  1191  	return store.Do(func(result *store.StoreResult) {
  1192  		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 {
  1193  			result.Err = model.NewAppError("SqlUserStore.AnalyticsGetSystemAdminCount", "store.sql_user.analytics_get_system_admin_count.app_error", nil, err.Error(), http.StatusInternalServerError)
  1194  		} else {
  1195  			result.Data = count
  1196  		}
  1197  	})
  1198  }
  1199  
  1200  func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int) store.StoreChannel {
  1201  	return store.Do(func(result *store.StoreResult) {
  1202  		var users []*model.User
  1203  
  1204  		if _, err := us.GetReplica().Select(&users, `
  1205              SELECT
  1206                  u.*
  1207              FROM Users u
  1208              LEFT JOIN TeamMembers tm
  1209                  ON tm.UserId = u.Id
  1210                  AND tm.TeamId = :TeamId
  1211                  AND tm.DeleteAt = 0
  1212              WHERE tm.UserId IS NULL
  1213              ORDER BY u.Username ASC
  1214              LIMIT :Limit OFFSET :Offset
  1215              `, map[string]interface{}{"TeamId": teamId, "Offset": offset, "Limit": limit}); err != nil {
  1216  			result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInTeam", "store.sql_user.get_profiles.app_error", nil, err.Error(), http.StatusInternalServerError)
  1217  		} else {
  1218  
  1219  			for _, u := range users {
  1220  				u.Sanitize(map[string]bool{})
  1221  			}
  1222  
  1223  			result.Data = users
  1224  		}
  1225  	})
  1226  }
  1227  
  1228  func (us SqlUserStore) GetEtagForProfilesNotInTeam(teamId string) store.StoreChannel {
  1229  	return store.Do(func(result *store.StoreResult) {
  1230  		updateAt, err := us.GetReplica().SelectInt(`
  1231              SELECT
  1232                  u.UpdateAt
  1233              FROM Users u
  1234              LEFT JOIN TeamMembers tm
  1235                  ON tm.UserId = u.Id
  1236                  AND tm.TeamId = :TeamId
  1237                  AND tm.DeleteAt = 0
  1238              WHERE tm.UserId IS NULL
  1239              ORDER BY u.UpdateAt DESC
  1240              LIMIT 1
  1241              `, map[string]interface{}{"TeamId": teamId})
  1242  
  1243  		if err != nil {
  1244  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, model.GetMillis())
  1245  		} else {
  1246  			result.Data = fmt.Sprintf("%v.%v", model.CurrentVersion, updateAt)
  1247  		}
  1248  	})
  1249  }