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