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