// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. package store import ( "crypto/md5" "database/sql" "fmt" "strconv" "strings" "time" l4g "github.com/alecthomas/log4go" "github.com/mattermost/platform/model" "github.com/mattermost/platform/utils" ) const ( MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const" MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error" ) type SqlUserStore struct { *SqlStore } func NewSqlUserStore(sqlStore *SqlStore) UserStore { us := &SqlUserStore{sqlStore} for _, db := range sqlStore.GetAllConns() { table := db.AddTableWithName(model.User{}, "Users").SetKeys(false, "Id") table.ColMap("Id").SetMaxSize(26) table.ColMap("Username").SetMaxSize(64).SetUnique(true) table.ColMap("Password").SetMaxSize(128) table.ColMap("AuthData").SetMaxSize(128).SetUnique(true) table.ColMap("AuthService").SetMaxSize(32) table.ColMap("Email").SetMaxSize(128).SetUnique(true) table.ColMap("Nickname").SetMaxSize(64) table.ColMap("FirstName").SetMaxSize(64) table.ColMap("LastName").SetMaxSize(64) table.ColMap("Roles").SetMaxSize(64) table.ColMap("Props").SetMaxSize(4000) table.ColMap("NotifyProps").SetMaxSize(2000) table.ColMap("Locale").SetMaxSize(5) table.ColMap("MfaSecret").SetMaxSize(128) } return us } func (us SqlUserStore) UpgradeSchemaIfNeeded() { // ADDED for 2.0 REMOVE for 2.4 us.CreateColumnIfNotExists("Users", "Locale", "varchar(5)", "character varying(5)", model.DEFAULT_LOCALE) // ADDED for 3.2 REMOVE for 3.6 if us.DoesColumnExist("Users", "ThemeProps") { params := map[string]interface{}{ "Category": model.PREFERENCE_CATEGORY_THEME, "Name": "", } transaction, err := us.GetMaster().Begin() if err != nil { themeMigrationFailed(err) } // increase size of Value column of Preferences table to match the size of the ThemeProps column if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_POSTGRES { if _, err := transaction.Exec("ALTER TABLE Preferences ALTER COLUMN Value TYPE varchar(2000)"); err != nil { themeMigrationFailed(err) } } else if utils.Cfg.SqlSettings.DriverName == model.DATABASE_DRIVER_MYSQL { if _, err := transaction.Exec("ALTER TABLE Preferences MODIFY Value text"); err != nil { themeMigrationFailed(err) } } // copy data across if _, err := transaction.Exec( `INSERT INTO Preferences(UserId, Category, Name, Value) SELECT Id, '`+model.PREFERENCE_CATEGORY_THEME+`', '', ThemeProps FROM Users WHERE Users.ThemeProps != 'null'`, params); err != nil { themeMigrationFailed(err) } // delete old data if _, err := transaction.Exec("ALTER TABLE Users DROP COLUMN ThemeProps"); err != nil { themeMigrationFailed(err) } if err := transaction.Commit(); err != nil { themeMigrationFailed(err) } // rename solarized_* code themes to solarized-* to match client changes in 3.0 var data model.Preferences if _, err := us.GetReplica().Select(&data, "SELECT * FROM Preferences WHERE Category = '"+model.PREFERENCE_CATEGORY_THEME+"' AND Value LIKE '%solarized_%'"); err == nil { for i := range data { data[i].Value = strings.Replace(data[i].Value, "solarized_", "solarized-", -1) } us.Preference().Save(&data) } } // ADDED for 3.3 remove for 3.7 us.RemoveColumnIfExists("Users", "LastActivityAt") us.RemoveColumnIfExists("Users", "LastPingAt") } func themeMigrationFailed(err error) { l4g.Critical(utils.T("store.sql_user.migrate_theme.critical"), err) time.Sleep(time.Second) panic(fmt.Sprintf(utils.T("store.sql_user.migrate_theme.critical"), err.Error())) } func (us SqlUserStore) CreateIndexesIfNotExists() { us.CreateIndexIfNotExists("idx_users_email", "Users", "Email") } func (us SqlUserStore) Save(user *model.User) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if len(user.Id) > 0 { result.Err = model.NewLocAppError("SqlUserStore.Save", "store.sql_user.save.existing.app_error", nil, "user_id="+user.Id) storeChannel <- result close(storeChannel) return } user.PreSave() if result.Err = user.IsValid(); result.Err != nil { storeChannel <- result close(storeChannel) return } if err := us.GetMaster().Insert(user); err != nil { if IsUniqueConstraintError(err.Error(), []string{"Email", "users_email_key", "idx_users_email_unique"}) { result.Err = model.NewLocAppError("SqlUserStore.Save", "store.sql_user.save.email_exists.app_error", nil, "user_id="+user.Id+", "+err.Error()) } else if IsUniqueConstraintError(err.Error(), []string{"Username", "users_username_key", "idx_users_username_unique"}) { result.Err = model.NewLocAppError("SqlUserStore.Save", "store.sql_user.save.username_exists.app_error", nil, "user_id="+user.Id+", "+err.Error()) } else { result.Err = model.NewLocAppError("SqlUserStore.Save", "store.sql_user.save.app_error", nil, "user_id="+user.Id+", "+err.Error()) } } else { result.Data = user } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) Update(user *model.User, trustedUpdateData bool) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} user.PreUpdate() if result.Err = user.IsValid(); result.Err != nil { storeChannel <- result close(storeChannel) return } if oldUserResult, err := us.GetMaster().Get(model.User{}, user.Id); err != nil { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.finding.app_error", nil, "user_id="+user.Id+", "+err.Error()) } else if oldUserResult == nil { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.find.app_error", nil, "user_id="+user.Id) } else { oldUser := oldUserResult.(*model.User) user.CreateAt = oldUser.CreateAt user.AuthData = oldUser.AuthData user.AuthService = oldUser.AuthService user.Password = oldUser.Password user.LastPasswordUpdate = oldUser.LastPasswordUpdate user.LastPictureUpdate = oldUser.LastPictureUpdate user.EmailVerified = oldUser.EmailVerified user.FailedAttempts = oldUser.FailedAttempts user.MfaSecret = oldUser.MfaSecret user.MfaActive = oldUser.MfaActive if !trustedUpdateData { user.Roles = oldUser.Roles user.DeleteAt = oldUser.DeleteAt } if user.IsOAuthUser() { user.Email = oldUser.Email } else if user.IsLDAPUser() && !trustedUpdateData { if user.Username != oldUser.Username || user.FirstName != oldUser.FirstName || user.LastName != oldUser.LastName || user.Email != oldUser.Email { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.can_not_change_ldap.app_error", nil, "user_id="+user.Id) storeChannel <- result close(storeChannel) return } } else if user.Email != oldUser.Email { user.EmailVerified = false } if user.Username != oldUser.Username { user.UpdateMentionKeysFromUsername(oldUser.Username) } if count, err := us.GetMaster().Update(user); err != nil { if IsUniqueConstraintError(err.Error(), []string{"Email", "users_email_key", "idx_users_email_unique"}) { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.email_taken.app_error", nil, "user_id="+user.Id+", "+err.Error()) } else if IsUniqueConstraintError(err.Error(), []string{"Username", "users_username_key", "idx_users_username_unique"}) { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.username_taken.app_error", nil, "user_id="+user.Id+", "+err.Error()) } else { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.updating.app_error", nil, "user_id="+user.Id+", "+err.Error()) } } else if count != 1 { result.Err = model.NewLocAppError("SqlUserStore.Update", "store.sql_user.update.app_error", nil, fmt.Sprintf("user_id=%v, count=%v", user.Id, count)) } else { result.Data = [2]*model.User{user, oldUser} } } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateLastPictureUpdate(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} curTime := model.GetMillis() if _, err := us.GetMaster().Exec("UPDATE Users SET LastPictureUpdate = :Time, UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.UpdateUpdateAt", "store.sql_user.update_last_picture_update.app_error", nil, "user_id="+userId) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateUpdateAt(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} curTime := model.GetMillis() if _, err := us.GetMaster().Exec("UPDATE Users SET UpdateAt = :Time WHERE Id = :UserId", map[string]interface{}{"Time": curTime, "UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.UpdateUpdateAt", "store.sql_user.update_update.app_error", nil, "user_id="+userId) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdatePassword(userId, hashedPassword string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} updateAt := model.GetMillis() 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 { result.Err = model.NewLocAppError("SqlUserStore.UpdatePassword", "store.sql_user.update_password.app_error", nil, "id="+userId+", "+err.Error()) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateFailedPasswordAttempts(userId string, attempts int) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if _, err := us.GetMaster().Exec("UPDATE Users SET FailedAttempts = :FailedAttempts WHERE Id = :UserId", map[string]interface{}{"FailedAttempts": attempts, "UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.UpdateFailedPasswordAttempts", "store.sql_user.update_failed_pwd_attempts.app_error", nil, "user_id="+userId) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateAuthData(userId string, service string, authData *string, email string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} email = strings.ToLower(email) updateAt := model.GetMillis() query := ` UPDATE Users SET Password = '', LastPasswordUpdate = :LastPasswordUpdate, UpdateAt = :UpdateAt, FailedAttempts = 0, AuthService = :AuthService, AuthData = :AuthData` if len(email) != 0 { query += ", Email = :Email" } query += " WHERE Id = :UserId" if _, err := us.GetMaster().Exec(query, map[string]interface{}{"LastPasswordUpdate": updateAt, "UpdateAt": updateAt, "UserId": userId, "AuthService": service, "AuthData": authData, "Email": email}); err != nil { if IsUniqueConstraintError(err.Error(), []string{"Email", "users_email_key", "idx_users_email_unique"}) { result.Err = model.NewLocAppError("SqlUserStore.UpdateAuthData", "store.sql_user.update_auth_data.email_exists.app_error", map[string]interface{}{"Service": service, "Email": email}, "user_id="+userId+", "+err.Error()) } else { result.Err = model.NewLocAppError("SqlUserStore.UpdateAuthData", "store.sql_user.update_auth_data.app_error", nil, "id="+userId+", "+err.Error()) } } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateMfaSecret(userId, secret string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} updateAt := model.GetMillis() 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 { result.Err = model.NewLocAppError("SqlUserStore.UpdateMfaSecret", "store.sql_user.update_mfa_secret.app_error", nil, "id="+userId+", "+err.Error()) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) UpdateMfaActive(userId string, active bool) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} updateAt := model.GetMillis() 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 { result.Err = model.NewLocAppError("SqlUserStore.UpdateMfaActive", "store.sql_user.update_mfa_active.app_error", nil, "id="+userId+", "+err.Error()) } else { result.Data = userId } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) Get(id string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if obj, err := us.GetReplica().Get(model.User{}, id); err != nil { result.Err = model.NewLocAppError("SqlUserStore.Get", "store.sql_user.get.app_error", nil, "user_id="+id+", "+err.Error()) } else if obj == nil { result.Err = model.NewLocAppError("SqlUserStore.Get", MISSING_ACCOUNT_ERROR, nil, "user_id="+id) } else { result.Data = obj.(*model.User) } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetAll() StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var data []*model.User if _, err := us.GetReplica().Select(&data, "SELECT * FROM Users"); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetAll", "store.sql_user.get.app_error", nil, err.Error()) } result.Data = data storeChannel <- result close(storeChannel) }() return storeChannel } func (s SqlUserStore) GetEtagForDirectProfiles(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var ids []string _, err := s.GetReplica().Select(ids, ` SELECT Id FROM Users WHERE Id IN (SELECT DISTINCT UserId FROM ChannelMembers WHERE ChannelMembers.UserId != :UserId AND ChannelMembers.ChannelId IN (SELECT Channels.Id FROM Channels, ChannelMembers WHERE Channels.Type = 'D' AND Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.UserId = :UserId)) OR Id IN (SELECT Name FROM Preferences WHERE UserId = :UserId AND Category = 'direct_channel_show') ORDER BY UpdateAt DESC `, map[string]interface{}{"UserId": userId}) if err != nil || len(ids) == 0 { result.Data = fmt.Sprintf("%v.%v.0.%v.%v", model.CurrentVersion, model.GetMillis(), utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } else { allIds := strings.Join(ids, "") result.Data = fmt.Sprintf("%v.%x.%v.%v.%v", model.CurrentVersion, md5.Sum([]byte(allIds)), len(ids), utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } storeChannel <- result close(storeChannel) }() return storeChannel } func (s SqlUserStore) GetEtagForAllProfiles() StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users ORDER BY UpdateAt DESC LIMIT 1") if err != nil { result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.GetMillis(), utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } else { result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, updateAt, utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetAllProfiles() StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var users []*model.User if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users"); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetProfiles", "store.sql_user.get_profiles.app_error", nil, err.Error()) } else { userMap := make(map[string]*model.User) for _, u := range users { u.Password = "" u.AuthData = new(string) *u.AuthData = "" userMap[u.Id] = u } result.Data = userMap } storeChannel <- result close(storeChannel) }() return storeChannel } func (s SqlUserStore) GetEtagForProfiles(teamId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} 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}) if err != nil { result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, model.GetMillis(), utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } else { result.Data = fmt.Sprintf("%v.%v.%v.%v", model.CurrentVersion, updateAt, utils.Cfg.PrivacySettings.ShowFullName, utils.Cfg.PrivacySettings.ShowEmailAddress) } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetProfiles(teamId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var users []*model.User if _, err := us.GetReplica().Select(&users, "SELECT Users.* FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId", map[string]interface{}{"TeamId": teamId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetProfiles", "store.sql_user.get_profiles.app_error", nil, err.Error()) } else { userMap := make(map[string]*model.User) for _, u := range users { u.Password = "" u.AuthData = new(string) *u.AuthData = "" userMap[u.Id] = u } result.Data = userMap } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetDirectProfiles(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var users []*model.User if _, err := us.GetReplica().Select(&users, ` SELECT Users.* FROM Users WHERE Id IN (SELECT DISTINCT UserId FROM ChannelMembers WHERE ChannelMembers.UserId != :UserId AND ChannelMembers.ChannelId IN (SELECT Channels.Id FROM Channels, ChannelMembers WHERE Channels.Type = 'D' AND Channels.Id = ChannelMembers.ChannelId AND ChannelMembers.UserId = :UserId)) OR Id IN (SELECT Name FROM Preferences WHERE UserId = :UserId AND Category = 'direct_channel_show') `, map[string]interface{}{"UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetDirectProfiles", "store.sql_user.get_profiles.app_error", nil, err.Error()) } else { userMap := make(map[string]*model.User) for _, u := range users { u.Password = "" u.AuthData = new(string) *u.AuthData = "" userMap[u.Id] = u } result.Data = userMap } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetProfileByIds(userIds []string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var users []*model.User props := make(map[string]interface{}) idQuery := "" for index, userId := range userIds { if len(idQuery) > 0 { idQuery += ", " } props["userId"+strconv.Itoa(index)] = userId idQuery += ":userId" + strconv.Itoa(index) } if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Users.Id IN ("+idQuery+")", props); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetProfileByIds", "store.sql_user.get_profiles.app_error", nil, err.Error()) } else { userMap := make(map[string]*model.User) for _, u := range users { u.Password = "" u.AuthData = new(string) *u.AuthData = "" userMap[u.Id] = u } result.Data = userMap } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetSystemAdminProfiles() StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var users []*model.User if _, err := us.GetReplica().Select(&users, "SELECT * FROM Users WHERE Roles = :Roles", map[string]interface{}{"Roles": "system_admin"}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetSystemAdminProfiles", "store.sql_user.get_sysadmin_profiles.app_error", nil, err.Error()) } else { userMap := make(map[string]*model.User) for _, u := range users { u.Password = "" u.AuthData = new(string) *u.AuthData = "" userMap[u.Id] = u } result.Data = userMap } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetByEmail(email string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} email = strings.ToLower(email) user := model.User{} if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE Email = :Email", map[string]interface{}{"Email": email}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetByEmail", MISSING_ACCOUNT_ERROR, nil, "email="+email+", "+err.Error()) } result.Data = &user storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetByAuth(authData *string, authService string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if authData == nil || *authData == "" { result.Err = model.NewLocAppError("SqlUserStore.GetByAuth", MISSING_AUTH_ACCOUNT_ERROR, nil, "authData='', authService="+authService) storeChannel <- result close(storeChannel) return } user := model.User{} if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE AuthData = :AuthData AND AuthService = :AuthService", map[string]interface{}{"AuthData": authData, "AuthService": authService}); err != nil { if err == sql.ErrNoRows { result.Err = model.NewLocAppError("SqlUserStore.GetByAuth", MISSING_AUTH_ACCOUNT_ERROR, nil, "authData="+*authData+", authService="+authService+", "+err.Error()) } else { result.Err = model.NewLocAppError("SqlUserStore.GetByAuth", "store.sql_user.get_by_auth.other.app_error", nil, "authData="+*authData+", authService="+authService+", "+err.Error()) } } result.Data = &user storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetAllUsingAuthService(authService string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} var data []*model.User if _, err := us.GetReplica().Select(&data, "SELECT * FROM Users WHERE AuthService = :AuthService", map[string]interface{}{"AuthService": authService}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetByAuth", "store.sql_user.get_by_auth.other.app_error", nil, "authService="+authService+", "+err.Error()) } result.Data = data storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetByUsername(username string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} user := model.User{} if err := us.GetReplica().SelectOne(&user, "SELECT * FROM Users WHERE Username = :Username", map[string]interface{}{"Username": username}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetByUsername", "store.sql_user.get_by_username.app_error", nil, err.Error()) } result.Data = &user storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetForLogin(loginId string, allowSignInWithUsername, allowSignInWithEmail, ldapEnabled bool) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} params := map[string]interface{}{ "LoginId": loginId, "AllowSignInWithUsername": allowSignInWithUsername, "AllowSignInWithEmail": allowSignInWithEmail, "LdapEnabled": ldapEnabled, } users := []*model.User{} if _, err := us.GetReplica().Select( &users, `SELECT * FROM Users WHERE (:AllowSignInWithUsername AND Username = :LoginId) OR (:AllowSignInWithEmail AND Email = :LoginId) OR (:LdapEnabled AND AuthService = '`+model.USER_AUTH_SERVICE_LDAP+`' AND AuthData = :LoginId)`, params); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, err.Error()) } else if len(users) == 1 { result.Data = users[0] } else if len(users) > 1 { result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.multiple_users", nil, "") } else { result.Err = model.NewLocAppError("SqlUserStore.GetForLogin", "store.sql_user.get_for_login.app_error", nil, "") } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) VerifyEmail(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if _, err := us.GetMaster().Exec("UPDATE Users SET EmailVerified = true WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.VerifyEmail", "store.sql_user.verify_email.app_error", nil, "userId="+userId+", "+err.Error()) } result.Data = userId storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetTotalUsersCount() StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if count, err := us.GetReplica().SelectInt("SELECT COUNT(Id) FROM Users"); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetTotalUsersCount", "store.sql_user.get_total_users_count.app_error", nil, err.Error()) } else { result.Data = count } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) PermanentDelete(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if _, err := us.GetMaster().Exec("DELETE FROM Users WHERE Id = :UserId", map[string]interface{}{"UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.PermanentDelete", "store.sql_user.permanent_delete.app_error", nil, "userId="+userId+", "+err.Error()) } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) AnalyticsUniqueUserCount(teamId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} query := "SELECT COUNT(DISTINCT Email) FROM Users" if len(teamId) > 0 { query += ", TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId" } v, err := us.GetReplica().SelectInt(query, map[string]interface{}{"TeamId": teamId}) if err != nil { result.Err = model.NewLocAppError("SqlUserStore.AnalyticsUniqueUserCount", "store.sql_user.analytics_unique_user_count.app_error", nil, err.Error()) } else { result.Data = v } storeChannel <- result close(storeChannel) }() return storeChannel } func (us SqlUserStore) GetUnreadCount(userId string) StoreChannel { storeChannel := make(StoreChannel) go func() { result := StoreResult{} if count, err := us.GetReplica().SelectInt("SELECT SUM(CASE WHEN c.Type = 'D' THEN (c.TotalMsgCount - cm.MsgCount) ELSE 0 END + cm.MentionCount) FROM Channels c INNER JOIN ChannelMembers cm ON cm.ChannelId = c.Id AND cm.UserId = :UserId", map[string]interface{}{"UserId": userId}); err != nil { result.Err = model.NewLocAppError("SqlUserStore.GetMentionCount", "store.sql_user.get_unread_count.app_error", nil, err.Error()) } else { result.Data = count } storeChannel <- result close(storeChannel) }() return storeChannel }