diff options
-rw-r--r-- | api4/user.go | 5 | ||||
-rw-r--r-- | api4/user_test.go | 40 | ||||
-rw-r--r-- | app/user.go | 16 | ||||
-rw-r--r-- | model/user_search.go | 1 | ||||
-rw-r--r-- | store/sql_user_store.go | 26 | ||||
-rw-r--r-- | store/sql_user_store_test.go | 41 | ||||
-rw-r--r-- | store/store.go | 1 |
7 files changed, 130 insertions, 0 deletions
diff --git a/api4/user.go b/api4/user.go index e4595ee54..593d704a5 100644 --- a/api4/user.go +++ b/api4/user.go @@ -397,6 +397,11 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) { return } + if props.NotInTeamId != "" && !app.SessionHasPermissionToTeam(c.Session, props.NotInTeamId, model.PERMISSION_VIEW_TEAM) { + c.SetPermissionError(model.PERMISSION_VIEW_TEAM) + return + } + searchOptions := map[string]bool{} searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = props.AllowInactive diff --git a/api4/user_test.go b/api4/user_test.go index 220ed124f..b3e4edc3d 100644 --- a/api4/user_test.go +++ b/api4/user_test.go @@ -385,6 +385,45 @@ func TestSearchUsers(t *testing.T) { _, resp = Client.SearchUsers(search) CheckForbiddenStatus(t, resp) + // Test search for users not in any team + search.TeamId = "" + search.NotInChannelId = "" + search.InChannelId = "" + search.NotInTeamId = th.BasicTeam.Id + + users, resp = Client.SearchUsers(search) + CheckNoError(t, resp) + + if findUserInList(th.BasicUser.Id, users) { + t.Fatal("should not have found user") + } + + oddUser := th.CreateUser() + search.Term = oddUser.Username + + users, resp = Client.SearchUsers(search) + CheckNoError(t, resp) + + if !findUserInList(oddUser.Id, users) { + t.Fatal("should have found user") + } + + _, resp = th.SystemAdminClient.AddTeamMember(th.BasicTeam.Id, oddUser.Id, "", "", th.BasicTeam.InviteId) + CheckNoError(t, resp) + + users, resp = Client.SearchUsers(search) + CheckNoError(t, resp) + + if findUserInList(oddUser.Id, users) { + t.Fatal("should not have found user") + } + + search.NotInTeamId = model.NewId() + _, resp = Client.SearchUsers(search) + CheckForbiddenStatus(t, resp) + + search.Term = th.BasicUser.Username + emailPrivacy := utils.Cfg.PrivacySettings.ShowEmailAddress namePrivacy := utils.Cfg.PrivacySettings.ShowFullName defer func() { @@ -400,6 +439,7 @@ func TestSearchUsers(t *testing.T) { } search.InChannelId = "" + search.NotInTeamId = "" search.Term = th.BasicUser2.Email users, resp = Client.SearchUsers(search) CheckNoError(t, resp) diff --git a/app/user.go b/app/user.go index 8e0615508..00f6df471 100644 --- a/app/user.go +++ b/app/user.go @@ -1299,6 +1299,8 @@ func SearchUsers(props *model.UserSearch, searchOptions map[string]bool, asAdmin return SearchUsersInChannel(props.InChannelId, props.Term, searchOptions, asAdmin) } else if props.NotInChannelId != "" { return SearchUsersNotInChannel(props.TeamId, props.NotInChannelId, props.Term, searchOptions, asAdmin) + } else if props.NotInTeamId != "" { + return SearchUsersNotInTeam(props.NotInTeamId, props.Term, searchOptions, asAdmin) } else { return SearchUsersInTeam(props.TeamId, props.Term, searchOptions, asAdmin) } @@ -1346,6 +1348,20 @@ func SearchUsersInTeam(teamId string, term string, searchOptions map[string]bool } } +func SearchUsersNotInTeam(notInTeamId string, term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) { + if result := <-Srv.Store.User().SearchNotInTeam(notInTeamId, term, searchOptions); result.Err != nil { + return nil, result.Err + } else { + users := result.Data.([]*model.User) + + for _, user := range users { + SanitizeProfile(user, asAdmin) + } + + return users, nil + } +} + func SearchUsersWithoutTeam(term string, searchOptions map[string]bool, asAdmin bool) ([]*model.User, *model.AppError) { if result := <-Srv.Store.User().SearchWithoutTeam(term, searchOptions); result.Err != nil { return nil, result.Err diff --git a/model/user_search.go b/model/user_search.go index 1ef881130..8d6a5c567 100644 --- a/model/user_search.go +++ b/model/user_search.go @@ -11,6 +11,7 @@ import ( type UserSearch struct { Term string `json:"term"` TeamId string `json:"team_id"` + NotInTeamId string `json:"not_in_team_id"` InChannelId string `json:"in_channel_id"` NotInChannelId string `json:"not_in_channel_id"` AllowInactive bool `json:"allow_inactive"` diff --git a/store/sql_user_store.go b/store/sql_user_store.go index 2fb7158ac..bcbd850c8 100644 --- a/store/sql_user_store.go +++ b/store/sql_user_store.go @@ -1339,6 +1339,32 @@ func (us SqlUserStore) SearchWithoutTeam(term string, options map[string]bool) S return storeChannel } +func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options map[string]bool) StoreChannel { + storeChannel := make(StoreChannel, 1) + + go func() { + searchQuery := ` + SELECT + Users.* + FROM Users + LEFT JOIN TeamMembers tm + ON tm.UserId = Users.Id + AND tm.TeamId = :NotInTeamId + WHERE + tm.UserId IS NULL + SEARCH_CLAUSE + INACTIVE_CLAUSE + ORDER BY Users.Username ASC + LIMIT 100` + + storeChannel <- us.performSearch(searchQuery, term, options, map[string]interface{}{"NotInTeamId": notInTeamId}) + close(storeChannel) + + }() + + return storeChannel +} + func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) StoreChannel { storeChannel := make(StoreChannel, 1) diff --git a/store/sql_user_store_test.go b/store/sql_user_store_test.go index 6e9642b60..db1ade5f7 100644 --- a/store/sql_user_store_test.go +++ b/store/sql_user_store_test.go @@ -1546,6 +1546,47 @@ func TestUserStoreSearch(t *testing.T) { t.Fatal("should have found user") } } + + // Search Users not in Team. + u4 := &model.User{} + u4.Username = "simon" + model.NewId() + u4.Email = model.NewId() + u4.DeleteAt = 0 + Must(store.User().Save(u4)) + + if r1 := <-store.User().SearchNotInTeam(tid, "simo", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u4.Id { + found = true + break + } + } + + if !found { + t.Fatal("should have found user") + } + } + + if r1 := <-store.User().SearchNotInTeam(tid, "jimb", searchOptions); r1.Err != nil { + t.Fatal(r1.Err) + } else { + profiles := r1.Data.([]*model.User) + found := false + for _, profile := range profiles { + if profile.Id == u1.Id { + found = true + break + } + } + + if found { + t.Fatal("should not have found user") + } + } } func TestUserStoreSearchWithoutTeam(t *testing.T) { diff --git a/store/store.go b/store/store.go index 079268b08..557ae443c 100644 --- a/store/store.go +++ b/store/store.go @@ -202,6 +202,7 @@ type UserStore interface { GetUnreadCountForChannel(userId string, channelId string) StoreChannel GetRecentlyActiveUsersForTeam(teamId string) StoreChannel Search(teamId string, term string, options map[string]bool) StoreChannel + SearchNotInTeam(notInTeamId string, term string, options map[string]bool) StoreChannel SearchInChannel(channelId string, term string, options map[string]bool) StoreChannel SearchNotInChannel(teamId string, channelId string, term string, options map[string]bool) StoreChannel SearchWithoutTeam(term string, options map[string]bool) StoreChannel |