summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api4/user.go5
-rw-r--r--api4/user_test.go40
-rw-r--r--app/user.go16
-rw-r--r--model/user_search.go1
-rw-r--r--store/sql_user_store.go26
-rw-r--r--store/sql_user_store_test.go41
-rw-r--r--store/store.go1
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