PLT-4710 User search now obeys privacy settings (#4673)
* Consider privacy settings in user search * Add sysadmin as exception to privacy settings for user search
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
c952985ffd
Коммит
ea26c72dad
33
api/user.go
33
api/user.go
@@ -2648,6 +2648,21 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
searchOptions := map[string]bool{}
|
searchOptions := map[string]bool{}
|
||||||
searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = props.AllowInactive
|
searchOptions[store.USER_SEARCH_OPTION_ALLOW_INACTIVE] = props.AllowInactive
|
||||||
|
|
||||||
|
if !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
hideFullName := !utils.Cfg.PrivacySettings.ShowFullName
|
||||||
|
hideEmail := !utils.Cfg.PrivacySettings.ShowEmailAddress
|
||||||
|
|
||||||
|
if hideFullName && hideEmail {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
||||||
|
} else if hideFullName {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_ALL_NO_FULL_NAME] = true
|
||||||
|
} else if hideEmail {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Err = nil
|
||||||
|
}
|
||||||
|
|
||||||
var uchan store.StoreChannel
|
var uchan store.StoreChannel
|
||||||
if props.InChannelId != "" {
|
if props.InChannelId != "" {
|
||||||
uchan = Srv.Store.User().SearchInChannel(props.InChannelId, props.Term, searchOptions)
|
uchan = Srv.Store.User().SearchInChannel(props.InChannelId, props.Term, searchOptions)
|
||||||
@@ -2711,7 +2726,14 @@ func autocompleteUsersInChannel(c *Context, w http.ResponseWriter, r *http.Reque
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchOptions := map[string]bool{}
|
searchOptions := map[string]bool{}
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
|
||||||
|
hideFullName := !utils.Cfg.PrivacySettings.ShowFullName
|
||||||
|
if hideFullName && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
||||||
|
c.Err = nil
|
||||||
|
} else {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
||||||
|
}
|
||||||
|
|
||||||
uchan := Srv.Store.User().SearchInChannel(channelId, term, searchOptions)
|
uchan := Srv.Store.User().SearchInChannel(channelId, term, searchOptions)
|
||||||
nuchan := Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions)
|
nuchan := Srv.Store.User().SearchNotInChannel(teamId, channelId, term, searchOptions)
|
||||||
@@ -2760,7 +2782,14 @@ func autocompleteUsersInTeam(c *Context, w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
searchOptions := map[string]bool{}
|
searchOptions := map[string]bool{}
|
||||||
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
|
||||||
|
hideFullName := !utils.Cfg.PrivacySettings.ShowFullName
|
||||||
|
if hideFullName && !HasPermissionToContext(c, model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME] = true
|
||||||
|
c.Err = nil
|
||||||
|
} else {
|
||||||
|
searchOptions[store.USER_SEARCH_OPTION_NAMES_ONLY] = true
|
||||||
|
}
|
||||||
|
|
||||||
uchan := Srv.Store.User().Search(teamId, term, searchOptions)
|
uchan := Srv.Store.User().Search(teamId, term, searchOptions)
|
||||||
|
|
||||||
|
|||||||
137
api/user_test.go
137
api/user_test.go
@@ -2240,6 +2240,112 @@ func TestSearchUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emailPrivacy := utils.Cfg.PrivacySettings.ShowEmailAddress
|
||||||
|
namePrivacy := utils.Cfg.PrivacySettings.ShowFullName
|
||||||
|
defer func() {
|
||||||
|
utils.Cfg.PrivacySettings.ShowEmailAddress = emailPrivacy
|
||||||
|
utils.Cfg.PrivacySettings.ShowFullName = namePrivacy
|
||||||
|
}()
|
||||||
|
utils.Cfg.PrivacySettings.ShowEmailAddress = false
|
||||||
|
utils.Cfg.PrivacySettings.ShowFullName = false
|
||||||
|
|
||||||
|
privacyEmailPrefix := strings.ToLower(model.NewId())
|
||||||
|
privacyUser := &model.User{Email: privacyEmailPrefix + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", FirstName: model.NewId(), LastName: "Jimmers"}
|
||||||
|
privacyUser = Client.Must(Client.CreateUser(privacyUser, "")).Data.(*model.User)
|
||||||
|
LinkUserToTeam(privacyUser, th.BasicTeam)
|
||||||
|
|
||||||
|
if result, err := Client.SearchUsers(model.UserSearch{Term: privacyUser.FirstName}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Id == privacyUser.Id {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
t.Fatal("should not have found profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Cfg.PrivacySettings.ShowEmailAddress = true
|
||||||
|
|
||||||
|
if result, err := Client.SearchUsers(model.UserSearch{Term: privacyUser.FirstName}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Id == privacyUser.Id {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
t.Fatal("should not have found profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Cfg.PrivacySettings.ShowEmailAddress = false
|
||||||
|
utils.Cfg.PrivacySettings.ShowFullName = true
|
||||||
|
|
||||||
|
if result, err := Client.SearchUsers(model.UserSearch{Term: privacyUser.FirstName}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Id == privacyUser.Id {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Fatal("should have found profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := Client.SearchUsers(model.UserSearch{Term: privacyEmailPrefix}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Id == privacyUser.Id {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if found {
|
||||||
|
t.Fatal("should not have found profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.Cfg.PrivacySettings.ShowEmailAddress = true
|
||||||
|
|
||||||
|
if result, err := Client.SearchUsers(model.UserSearch{Term: privacyEmailPrefix}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
users := result.Data.([]*model.User)
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, user := range users {
|
||||||
|
if user.Id == privacyUser.Id {
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
t.Fatal("should have found profile")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
th.LoginBasic2()
|
th.LoginBasic2()
|
||||||
|
|
||||||
if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username}); err != nil {
|
if result, err := Client.SearchUsers(model.UserSearch{Term: th.BasicUser.Username}); err != nil {
|
||||||
@@ -2364,6 +2470,37 @@ func TestAutocompleteUsers(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namePrivacy := utils.Cfg.PrivacySettings.ShowFullName
|
||||||
|
defer func() {
|
||||||
|
utils.Cfg.PrivacySettings.ShowFullName = namePrivacy
|
||||||
|
}()
|
||||||
|
utils.Cfg.PrivacySettings.ShowFullName = false
|
||||||
|
|
||||||
|
privacyUser := &model.User{Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "passwd1", FirstName: model.NewId(), LastName: "Jimmers"}
|
||||||
|
privacyUser = Client.Must(Client.CreateUser(privacyUser, "")).Data.(*model.User)
|
||||||
|
LinkUserToTeam(privacyUser, th.BasicTeam)
|
||||||
|
|
||||||
|
if result, err := Client.AutocompleteUsersInChannel(privacyUser.FirstName, th.BasicChannel.Id); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
autocomplete := result.Data.(*model.UserAutocompleteInChannel)
|
||||||
|
if len(autocomplete.InChannel) != 0 {
|
||||||
|
t.Fatal("should have returned no users")
|
||||||
|
}
|
||||||
|
if len(autocomplete.OutOfChannel) != 0 {
|
||||||
|
t.Fatal("should have returned no users")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if result, err := Client.AutocompleteUsersInTeam(privacyUser.FirstName); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
} else {
|
||||||
|
autocomplete := result.Data.(*model.UserAutocompleteInTeam)
|
||||||
|
if len(autocomplete.InTeam) != 0 {
|
||||||
|
t.Fatal("should have returned no users")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := Client.AutocompleteUsersInChannel("", "junk"); err == nil {
|
if _, err := Client.AutocompleteUsersInChannel("", "junk"); err == nil {
|
||||||
t.Fatal("should have errored - bad channel id")
|
t.Fatal("should have errored - bad channel id")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,14 +15,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const"
|
MISSING_ACCOUNT_ERROR = "store.sql_user.missing_account.const"
|
||||||
MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
|
MISSING_AUTH_ACCOUNT_ERROR = "store.sql_user.get_by_auth.missing_account.app_error"
|
||||||
PROFILES_IN_CHANNEL_CACHE_SIZE = 5000
|
PROFILES_IN_CHANNEL_CACHE_SIZE = 5000
|
||||||
PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
|
PROFILES_IN_CHANNEL_CACHE_SEC = 900 // 15 mins
|
||||||
USER_SEARCH_OPTION_NAMES_ONLY = "names_only"
|
USER_SEARCH_OPTION_NAMES_ONLY = "names_only"
|
||||||
USER_SEARCH_OPTION_ALLOW_INACTIVE = "allow_inactive"
|
USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME = "names_only_no_full_name"
|
||||||
USER_SEARCH_TYPE_NAMES = "Username, FirstName, LastName, Nickname"
|
USER_SEARCH_OPTION_ALL_NO_FULL_NAME = "all_no_full_name"
|
||||||
USER_SEARCH_TYPE_ALL = "Username, FirstName, LastName, Nickname, Email"
|
USER_SEARCH_OPTION_ALLOW_INACTIVE = "allow_inactive"
|
||||||
|
USER_SEARCH_TYPE_NAMES_NO_FULL_NAME = "Username, Nickname"
|
||||||
|
USER_SEARCH_TYPE_NAMES = "Username, FirstName, LastName, Nickname"
|
||||||
|
USER_SEARCH_TYPE_ALL_NO_FULL_NAME = "Username, Nickname, Email"
|
||||||
|
USER_SEARCH_TYPE_ALL = "Username, FirstName, LastName, Nickname, Email"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SqlUserStore struct {
|
type SqlUserStore struct {
|
||||||
@@ -62,7 +66,9 @@ func (us SqlUserStore) CreateIndexesIfNotExists() {
|
|||||||
us.CreateIndexIfNotExists("idx_users_delete_at", "Users", "DeleteAt")
|
us.CreateIndexIfNotExists("idx_users_delete_at", "Users", "DeleteAt")
|
||||||
|
|
||||||
us.CreateFullTextIndexIfNotExists("idx_users_all_txt", "Users", USER_SEARCH_TYPE_ALL)
|
us.CreateFullTextIndexIfNotExists("idx_users_all_txt", "Users", USER_SEARCH_TYPE_ALL)
|
||||||
|
us.CreateFullTextIndexIfNotExists("idx_users_all_no_full_name_txt", "Users", USER_SEARCH_TYPE_ALL_NO_FULL_NAME)
|
||||||
us.CreateFullTextIndexIfNotExists("idx_users_names_txt", "Users", USER_SEARCH_TYPE_NAMES)
|
us.CreateFullTextIndexIfNotExists("idx_users_names_txt", "Users", USER_SEARCH_TYPE_NAMES)
|
||||||
|
us.CreateFullTextIndexIfNotExists("idx_users_names_no_full_name_txt", "Users", USER_SEARCH_TYPE_NAMES_NO_FULL_NAME)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) Save(user *model.User) StoreChannel {
|
func (us SqlUserStore) Save(user *model.User) StoreChannel {
|
||||||
@@ -1241,6 +1247,10 @@ func (us SqlUserStore) performSearch(searchQuery string, term string, options ma
|
|||||||
searchType := USER_SEARCH_TYPE_ALL
|
searchType := USER_SEARCH_TYPE_ALL
|
||||||
if ok := options[USER_SEARCH_OPTION_NAMES_ONLY]; ok {
|
if ok := options[USER_SEARCH_OPTION_NAMES_ONLY]; ok {
|
||||||
searchType = USER_SEARCH_TYPE_NAMES
|
searchType = USER_SEARCH_TYPE_NAMES
|
||||||
|
} else if ok = options[USER_SEARCH_OPTION_NAMES_ONLY_NO_FULL_NAME]; ok {
|
||||||
|
searchType = USER_SEARCH_TYPE_NAMES_NO_FULL_NAME
|
||||||
|
} else if ok = options[USER_SEARCH_OPTION_ALL_NO_FULL_NAME]; ok {
|
||||||
|
searchType = USER_SEARCH_TYPE_ALL_NO_FULL_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok := options[USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
|
if ok := options[USER_SEARCH_OPTION_ALLOW_INACTIVE]; ok {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user