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  }