github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/store/searchlayer/user_layer.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package searchlayer 5 6 import ( 7 "strings" 8 9 "github.com/mattermost/mattermost-server/v5/mlog" 10 "github.com/mattermost/mattermost-server/v5/model" 11 "github.com/mattermost/mattermost-server/v5/services/searchengine" 12 "github.com/mattermost/mattermost-server/v5/store" 13 ) 14 15 type SearchUserStore struct { 16 store.UserStore 17 rootStore *SearchStore 18 } 19 20 func (s *SearchUserStore) deleteUserIndex(user *model.User) { 21 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 22 if engine.IsIndexingEnabled() { 23 runIndexFn(engine, func(engineCopy searchengine.SearchEngineInterface) { 24 if err := engineCopy.DeleteUser(user); err != nil { 25 mlog.Error("Encountered error deleting user", mlog.String("user_id", user.Id), mlog.String("search_engine", engineCopy.GetName()), mlog.Err(err)) 26 return 27 } 28 mlog.Debug("Removed user from the index in search engine", mlog.String("search_engine", engineCopy.GetName()), mlog.String("user_id", user.Id)) 29 }) 30 } 31 } 32 } 33 34 func (s *SearchUserStore) Search(teamId, term string, options *model.UserSearchOptions) ([]*model.User, *model.AppError) { 35 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 36 if engine.IsSearchEnabled() { 37 listOfAllowedChannels, err := s.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) 38 if err != nil { 39 mlog.Error("Encountered error on Search.", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 40 continue 41 } 42 43 if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 { 44 return []*model.User{}, nil 45 } 46 47 sanitizedTerm := sanitizeSearchTerm(term) 48 49 usersIds, err := engine.SearchUsersInTeam(teamId, listOfAllowedChannels, sanitizedTerm, options) 50 if err != nil { 51 mlog.Error("Encountered error on Search", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 52 continue 53 } 54 55 users, err := s.UserStore.GetProfileByIds(usersIds, nil, false) 56 if err != nil { 57 mlog.Error("Encountered error on Search", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 58 continue 59 } 60 61 mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName())) 62 return users, nil 63 } 64 } 65 66 mlog.Debug("Using database search because no other search engine is available") 67 68 return s.UserStore.Search(teamId, term, options) 69 } 70 71 func (s *SearchUserStore) Update(user *model.User, trustedUpdateData bool) (*model.UserUpdate, *model.AppError) { 72 userUpdate, err := s.UserStore.Update(user, trustedUpdateData) 73 74 if err == nil { 75 s.rootStore.indexUser(userUpdate.New) 76 } 77 return userUpdate, err 78 } 79 80 func (s *SearchUserStore) Save(user *model.User) (*model.User, *model.AppError) { 81 nuser, err := s.UserStore.Save(user) 82 83 if err == nil { 84 s.rootStore.indexUser(nuser) 85 } 86 return nuser, err 87 } 88 89 func (s *SearchUserStore) PermanentDelete(userId string) *model.AppError { 90 user, userErr := s.UserStore.Get(userId) 91 if userErr != nil { 92 mlog.Error("Encountered error deleting user", mlog.String("user_id", userId), mlog.Err(userErr)) 93 } 94 err := s.UserStore.PermanentDelete(userId) 95 if err == nil && userErr == nil { 96 s.deleteUserIndex(user) 97 } 98 return err 99 } 100 101 func (s *SearchUserStore) autocompleteUsersInChannelByEngine(engine searchengine.SearchEngineInterface, teamId, channelId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) { 102 var err *model.AppError 103 uchanIds := []string{} 104 nuchanIds := []string{} 105 sanitizedTerm := sanitizeSearchTerm(term) 106 if channelId != "" && options.ListOfAllowedChannels != nil && !strings.Contains(strings.Join(options.ListOfAllowedChannels, "."), channelId) { 107 nuchanIds, err = engine.SearchUsersInTeam(teamId, options.ListOfAllowedChannels, sanitizedTerm, options) 108 } else { 109 uchanIds, nuchanIds, err = engine.SearchUsersInChannel(teamId, channelId, options.ListOfAllowedChannels, sanitizedTerm, options) 110 } 111 if err != nil { 112 return nil, err 113 } 114 115 uchan := make(chan store.StoreResult, 1) 116 go func() { 117 users, err := s.UserStore.GetProfileByIds(uchanIds, nil, false) 118 uchan <- store.StoreResult{Data: users, Err: err} 119 close(uchan) 120 }() 121 122 nuchan := make(chan store.StoreResult, 1) 123 go func() { 124 users, err := s.UserStore.GetProfileByIds(nuchanIds, nil, false) 125 nuchan <- store.StoreResult{Data: users, Err: err} 126 close(nuchan) 127 }() 128 129 autocomplete := &model.UserAutocompleteInChannel{} 130 131 result := <-uchan 132 if result.Err != nil { 133 return nil, result.Err 134 } 135 inUsers := result.Data.([]*model.User) 136 autocomplete.InChannel = inUsers 137 138 result = <-nuchan 139 if result.Err != nil { 140 return nil, result.Err 141 } 142 outUsers := result.Data.([]*model.User) 143 autocomplete.OutOfChannel = outUsers 144 145 return autocomplete, nil 146 } 147 148 // getListOfAllowedChannelsForTeam return the list of allowed channels to search user based on the 149 // next scenarios: 150 // - If there isn't view restrictions (team or channel) and no team id to filter them, then all 151 // channels are allowed (nil return) 152 // - If we receive a team Id and either we don't have view restrictions or the provided team id is included in the 153 // list of restricted teams, then we return all the team channels 154 // - If we don't receive team id or the provided team id is not in the list of allowed teams to search of and we 155 // don't have channel restrictions then we return an empty result because we cannot get channels 156 // - If we receive channels restrictions we get: 157 // - If we don't have team id, we get those restricted channels (guest accounts and quick search) 158 // - If we have a team id then we only return those restricted channels that belongs to that team 159 func (s *SearchUserStore) getListOfAllowedChannelsForTeam(teamId string, viewRestrictions *model.ViewUsersRestrictions) ([]string, *model.AppError) { 160 var listOfAllowedChannels []string 161 if viewRestrictions == nil && teamId == "" { 162 // nil return without error means all channels are allowed 163 return nil, nil 164 } 165 166 if teamId != "" && (viewRestrictions == nil || strings.Contains(strings.Join(viewRestrictions.Teams, "."), teamId)) { 167 channels, err := s.rootStore.Channel().GetTeamChannels(teamId) 168 if err != nil { 169 return nil, err 170 } 171 for _, channel := range *channels { 172 listOfAllowedChannels = append(listOfAllowedChannels, channel.Id) 173 } 174 return listOfAllowedChannels, nil 175 } 176 177 if len(viewRestrictions.Channels) > 0 { 178 channels, err := s.rootStore.Channel().GetChannelsByIds(viewRestrictions.Channels, false) 179 if err != nil { 180 return nil, err 181 } 182 for _, c := range channels { 183 if teamId == "" || (teamId != "" && c.TeamId == teamId) { 184 listOfAllowedChannels = append(listOfAllowedChannels, c.Id) 185 } 186 } 187 return listOfAllowedChannels, nil 188 } 189 190 return []string{}, nil 191 } 192 193 func (s *SearchUserStore) AutocompleteUsersInChannel(teamId, channelId, term string, options *model.UserSearchOptions) (*model.UserAutocompleteInChannel, *model.AppError) { 194 for _, engine := range s.rootStore.searchEngine.GetActiveEngines() { 195 if engine.IsAutocompletionEnabled() { 196 listOfAllowedChannels, err := s.getListOfAllowedChannelsForTeam(teamId, options.ViewRestrictions) 197 if err != nil { 198 mlog.Error("Encountered error on AutocompleteUsersInChannel.", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 199 continue 200 } 201 if listOfAllowedChannels != nil && len(listOfAllowedChannels) == 0 { 202 return &model.UserAutocompleteInChannel{}, nil 203 } 204 options.ListOfAllowedChannels = listOfAllowedChannels 205 autocomplete, err := s.autocompleteUsersInChannelByEngine(engine, teamId, channelId, term, options) 206 if err != nil { 207 mlog.Error("Encountered error on AutocompleteUsersInChannel.", mlog.String("search_engine", engine.GetName()), mlog.Err(err)) 208 continue 209 } 210 mlog.Debug("Using the first available search engine", mlog.String("search_engine", engine.GetName())) 211 return autocomplete, err 212 } 213 } 214 215 mlog.Debug("Using database search because no other search engine is available") 216 return s.UserStore.AutocompleteUsersInChannel(teamId, channelId, term, options) 217 }