[MM-26574] Add role filters to get users, users search and add getFilteredUserStats endpoint (#14998)
* MM-26574 Add role filters to user search and get * Add ability to get filtered user stats Add support for include bots * Add tests for user count with filters Add tests * Apply changes from code review * Fix guest filtering * Fix up tests related to guests * Clean role names * Trigger CI * Trigger CI
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c53c9ed190
Коммит
c0bfa58ec1
17
api4/role.go
17
api4/role.go
@@ -5,7 +5,6 @@ package api4
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/audit"
|
"github.com/mattermost/mattermost-server/v5/audit"
|
||||||
"github.com/mattermost/mattermost-server/v5/model"
|
"github.com/mattermost/mattermost-server/v5/model"
|
||||||
@@ -56,18 +55,10 @@ func getRolesByNames(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var cleanedRoleNames []string
|
cleanedRoleNames, valid := model.CleanRoleNames(rolenames)
|
||||||
for _, rolename := range rolenames {
|
if !valid {
|
||||||
if strings.TrimSpace(rolename) == "" {
|
c.SetInvalidParam("rolename")
|
||||||
continue
|
return
|
||||||
}
|
|
||||||
|
|
||||||
if !model.IsValidRoleName(rolename) {
|
|
||||||
c.SetInvalidParam("rolename")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanedRoleNames = append(cleanedRoleNames, rolename)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
roles, err := c.App.GetRolesByNames(cleanedRoleNames)
|
roles, err := c.App.GetRolesByNames(cleanedRoleNames)
|
||||||
|
|||||||
98
api4/user.go
98
api4/user.go
@@ -30,6 +30,7 @@ func (api *API) InitUser() {
|
|||||||
api.BaseRoutes.Users.Handle("/search", api.ApiSessionRequiredDisableWhenBusy(searchUsers)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/search", api.ApiSessionRequiredDisableWhenBusy(searchUsers)).Methods("POST")
|
||||||
api.BaseRoutes.Users.Handle("/autocomplete", api.ApiSessionRequired(autocompleteUsers)).Methods("GET")
|
api.BaseRoutes.Users.Handle("/autocomplete", api.ApiSessionRequired(autocompleteUsers)).Methods("GET")
|
||||||
api.BaseRoutes.Users.Handle("/stats", api.ApiSessionRequired(getTotalUsersStats)).Methods("GET")
|
api.BaseRoutes.Users.Handle("/stats", api.ApiSessionRequired(getTotalUsersStats)).Methods("GET")
|
||||||
|
api.BaseRoutes.Users.Handle("/stats/filtered", api.ApiSessionRequired(getFilteredUsersStats)).Methods("GET")
|
||||||
api.BaseRoutes.Users.Handle("/group_channels", api.ApiSessionRequired(getUsersByGroupChannelIds)).Methods("POST")
|
api.BaseRoutes.Users.Handle("/group_channels", api.ApiSessionRequired(getUsersByGroupChannelIds)).Methods("POST")
|
||||||
|
|
||||||
api.BaseRoutes.User.Handle("", api.ApiSessionRequired(getUser)).Methods("GET")
|
api.BaseRoutes.User.Handle("", api.ApiSessionRequired(getUser)).Methods("GET")
|
||||||
@@ -512,6 +513,68 @@ func getTotalUsersStats(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
w.Write([]byte(stats.ToJson()))
|
w.Write([]byte(stats.ToJson()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getFilteredUsersStats(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
|
teamID := r.URL.Query().Get("in_team")
|
||||||
|
channelID := r.URL.Query().Get("in_channel")
|
||||||
|
includeDeleted := r.URL.Query().Get("include_deleted")
|
||||||
|
includeBotAccounts := r.URL.Query().Get("include_bots")
|
||||||
|
rolesString := r.URL.Query().Get("roles")
|
||||||
|
channelRolesString := r.URL.Query().Get("channel_roles")
|
||||||
|
teamRolesString := r.URL.Query().Get("team_roles")
|
||||||
|
|
||||||
|
includeDeletedBool, _ := strconv.ParseBool(includeDeleted)
|
||||||
|
includeBotAccountsBool, _ := strconv.ParseBool(includeBotAccounts)
|
||||||
|
|
||||||
|
roles := []string{}
|
||||||
|
var rolesValid bool
|
||||||
|
if rolesString != "" {
|
||||||
|
roles, rolesValid = model.CleanRoleNames(strings.Split(rolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("roles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
channelRoles := []string{}
|
||||||
|
if channelRolesString != "" && len(channelID) != 0 {
|
||||||
|
channelRoles, rolesValid = model.CleanRoleNames(strings.Split(channelRolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("channelRoles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
teamRoles := []string{}
|
||||||
|
if teamRolesString != "" && len(teamID) != 0 {
|
||||||
|
teamRoles, rolesValid = model.CleanRoleNames(strings.Split(teamRolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("teamRoles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
options := &model.UserCountOptions{
|
||||||
|
IncludeDeleted: includeDeletedBool,
|
||||||
|
IncludeBotAccounts: includeBotAccountsBool,
|
||||||
|
TeamId: teamID,
|
||||||
|
ChannelId: channelID,
|
||||||
|
Roles: roles,
|
||||||
|
ChannelRoles: channelRoles,
|
||||||
|
TeamRoles: teamRoles,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
c.SetPermissionError(model.PERMISSION_MANAGE_SYSTEM)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := c.App.GetFilteredUsersStats(options)
|
||||||
|
if err != nil {
|
||||||
|
c.Err = err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write([]byte(stats.ToJson()))
|
||||||
|
}
|
||||||
|
|
||||||
func getUsersByGroupChannelIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
func getUsersByGroupChannelIds(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||||
channelIds := model.ArrayFromJson(r.Body)
|
channelIds := model.ArrayFromJson(r.Body)
|
||||||
|
|
||||||
@@ -542,6 +605,9 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
active := r.URL.Query().Get("active")
|
active := r.URL.Query().Get("active")
|
||||||
role := r.URL.Query().Get("role")
|
role := r.URL.Query().Get("role")
|
||||||
sort := r.URL.Query().Get("sort")
|
sort := r.URL.Query().Get("sort")
|
||||||
|
rolesString := r.URL.Query().Get("roles")
|
||||||
|
channelRolesString := r.URL.Query().Get("channel_roles")
|
||||||
|
teamRolesString := r.URL.Query().Get("team_roles")
|
||||||
|
|
||||||
if len(notInChannelId) > 0 && len(inTeamId) == 0 {
|
if len(notInChannelId) > 0 && len(inTeamId) == 0 {
|
||||||
c.SetInvalidUrlParam("team_id")
|
c.SetInvalidUrlParam("team_id")
|
||||||
@@ -573,6 +639,32 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
c.SetInvalidUrlParam("inactive")
|
c.SetInvalidUrlParam("inactive")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
roles := []string{}
|
||||||
|
var rolesValid bool
|
||||||
|
if rolesString != "" {
|
||||||
|
roles, rolesValid = model.CleanRoleNames(strings.Split(rolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("roles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
channelRoles := []string{}
|
||||||
|
if channelRolesString != "" && len(inChannelId) != 0 {
|
||||||
|
channelRoles, rolesValid = model.CleanRoleNames(strings.Split(channelRolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("channelRoles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
teamRoles := []string{}
|
||||||
|
if teamRolesString != "" && len(inTeamId) != 0 {
|
||||||
|
teamRoles, rolesValid = model.CleanRoleNames(strings.Split(teamRolesString, ","))
|
||||||
|
if !rolesValid {
|
||||||
|
c.SetInvalidParam("teamRoles")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
restrictions, err := c.App.GetViewUsersRestrictions(c.App.Session().UserId)
|
restrictions, err := c.App.GetViewUsersRestrictions(c.App.Session().UserId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Err = err
|
c.Err = err
|
||||||
@@ -590,6 +682,9 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
Inactive: inactiveBool,
|
Inactive: inactiveBool,
|
||||||
Active: activeBool,
|
Active: activeBool,
|
||||||
Role: role,
|
Role: role,
|
||||||
|
Roles: roles,
|
||||||
|
ChannelRoles: channelRoles,
|
||||||
|
TeamRoles: teamRoles,
|
||||||
Sort: sort,
|
Sort: sort,
|
||||||
Page: c.Params.Page,
|
Page: c.Params.Page,
|
||||||
PerPage: c.Params.PerPage,
|
PerPage: c.Params.PerPage,
|
||||||
@@ -824,6 +919,9 @@ func searchUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
|||||||
GroupConstrained: props.GroupConstrained,
|
GroupConstrained: props.GroupConstrained,
|
||||||
Limit: props.Limit,
|
Limit: props.Limit,
|
||||||
Role: props.Role,
|
Role: props.Role,
|
||||||
|
Roles: props.Roles,
|
||||||
|
ChannelRoles: props.ChannelRoles,
|
||||||
|
TeamRoles: props.TeamRoles,
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
if c.App.SessionHasPermissionTo(*c.App.Session(), model.PERMISSION_MANAGE_SYSTEM) {
|
||||||
|
|||||||
@@ -157,6 +157,8 @@ type AppIface interface {
|
|||||||
GetEmojiStaticUrl(emojiName string) (string, *model.AppError)
|
GetEmojiStaticUrl(emojiName string) (string, *model.AppError)
|
||||||
// GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
|
// GetEnvironmentConfig returns a map of configuration keys whose values have been overridden by an environment variable.
|
||||||
GetEnvironmentConfig() map[string]interface{}
|
GetEnvironmentConfig() map[string]interface{}
|
||||||
|
// GetFilteredUsersStats is used to get a count of users based on the set of filters supported by UserCountOptions.
|
||||||
|
GetFilteredUsersStats(options *model.UserCountOptions) (*model.UsersStats, *model.AppError)
|
||||||
// GetGroupsByTeam returns the paged list and the total count of group associated to the given team.
|
// GetGroupsByTeam returns the paged list and the total count of group associated to the given team.
|
||||||
GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.AppError)
|
GetGroupsByTeam(teamId string, opts model.GroupSearchOpts) ([]*model.GroupWithSchemeAdmin, int, *model.AppError)
|
||||||
// GetKnownUsers returns the list of user ids of users with any direct
|
// GetKnownUsers returns the list of user ids of users with any direct
|
||||||
|
|||||||
@@ -5158,6 +5158,28 @@ func (a *OpenTracingAppLayer) GetFileInfosForPostWithMigration(postId string) ([
|
|||||||
return resultVar0, resultVar1
|
return resultVar0, resultVar1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *OpenTracingAppLayer) GetFilteredUsersStats(options *model.UserCountOptions) (*model.UsersStats, *model.AppError) {
|
||||||
|
origCtx := a.ctx
|
||||||
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFilteredUsersStats")
|
||||||
|
|
||||||
|
a.ctx = newCtx
|
||||||
|
a.app.Srv().Store.SetContext(newCtx)
|
||||||
|
defer func() {
|
||||||
|
a.app.Srv().Store.SetContext(origCtx)
|
||||||
|
a.ctx = origCtx
|
||||||
|
}()
|
||||||
|
|
||||||
|
defer span.Finish()
|
||||||
|
resultVar0, resultVar1 := a.app.GetFilteredUsersStats(options)
|
||||||
|
|
||||||
|
if resultVar1 != nil {
|
||||||
|
span.LogFields(spanlog.Error(resultVar1))
|
||||||
|
ext.Error.Set(span, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultVar0, resultVar1
|
||||||
|
}
|
||||||
|
|
||||||
func (a *OpenTracingAppLayer) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) {
|
func (a *OpenTracingAppLayer) GetFlaggedPosts(userId string, offset int, limit int) (*model.PostList, *model.AppError) {
|
||||||
origCtx := a.ctx
|
origCtx := a.ctx
|
||||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFlaggedPosts")
|
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.GetFlaggedPosts")
|
||||||
|
|||||||
12
app/user.go
12
app/user.go
@@ -1649,6 +1649,18 @@ func (a *App) GetTotalUsersStats(viewRestrictions *model.ViewUsersRestrictions)
|
|||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFilteredUsersStats is used to get a count of users based on the set of filters supported by UserCountOptions.
|
||||||
|
func (a *App) GetFilteredUsersStats(options *model.UserCountOptions) (*model.UsersStats, *model.AppError) {
|
||||||
|
count, err := a.Srv().Store.User().Count(*options)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
stats := &model.UsersStats{
|
||||||
|
TotalUsersCount: count,
|
||||||
|
}
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) VerifyUserEmail(userId, email string) *model.AppError {
|
func (a *App) VerifyUserEmail(userId, email string) *model.AppError {
|
||||||
if _, err := a.Srv().Store.User().VerifyEmail(userId, email); err != nil {
|
if _, err := a.Srv().Store.User().VerifyEmail(userId, email); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -364,6 +364,23 @@ func (r *Role) IsValidWithoutId() bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CleanRoleNames(roleNames []string) ([]string, bool) {
|
||||||
|
var cleanedRoleNames []string
|
||||||
|
for _, roleName := range roleNames {
|
||||||
|
if strings.TrimSpace(roleName) == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !IsValidRoleName(roleName) {
|
||||||
|
return roleNames, false
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanedRoleNames = append(cleanedRoleNames, roleName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanedRoleNames, true
|
||||||
|
}
|
||||||
|
|
||||||
func IsValidRoleName(roleName string) bool {
|
func IsValidRoleName(roleName string) bool {
|
||||||
if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
|
if len(roleName) <= 0 || len(roleName) > ROLE_NAME_MAX_LENGTH {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ type UserCountOptions struct {
|
|||||||
ExcludeRegularUsers bool
|
ExcludeRegularUsers bool
|
||||||
// Only include users on a specific team. "" for any team.
|
// Only include users on a specific team. "" for any team.
|
||||||
TeamId string
|
TeamId string
|
||||||
|
// Only include users on a specific channel. "" for any channel.
|
||||||
|
ChannelId string
|
||||||
// Restrict to search in a list of teams and channels
|
// Restrict to search in a list of teams and channels
|
||||||
ViewRestrictions *ViewUsersRestrictions
|
ViewRestrictions *ViewUsersRestrictions
|
||||||
|
// Only include users matching any of the given system wide roles.
|
||||||
|
Roles []string
|
||||||
|
// Only include users matching any of the given channel roles, must be used with ChannelId.
|
||||||
|
ChannelRoles []string
|
||||||
|
// Only include users matching any of the given team roles, must be used with TeamId.
|
||||||
|
TeamRoles []string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ type UserGetOptions struct {
|
|||||||
Active bool
|
Active bool
|
||||||
// Filters for the given role
|
// Filters for the given role
|
||||||
Role string
|
Role string
|
||||||
|
// Filters for users matching any of the given system wide roles
|
||||||
|
Roles []string
|
||||||
|
// Filters for users matching any of the given channel roles, must be used with InChannelId
|
||||||
|
ChannelRoles []string
|
||||||
|
// Filters for users matching any of the given team roles, must be used with InTeamId
|
||||||
|
TeamRoles []string
|
||||||
// Sorting option
|
// Sorting option
|
||||||
Sort string
|
Sort string
|
||||||
// Restrict to search in a list of teams and channels
|
// Restrict to search in a list of teams and channels
|
||||||
|
|||||||
@@ -13,17 +13,20 @@ const USER_SEARCH_DEFAULT_LIMIT = 100
|
|||||||
|
|
||||||
// UserSearch captures the parameters provided by a client for initiating a user search.
|
// UserSearch captures the parameters provided by a client for initiating a user search.
|
||||||
type UserSearch struct {
|
type UserSearch struct {
|
||||||
Term string `json:"term"`
|
Term string `json:"term"`
|
||||||
TeamId string `json:"team_id"`
|
TeamId string `json:"team_id"`
|
||||||
NotInTeamId string `json:"not_in_team_id"`
|
NotInTeamId string `json:"not_in_team_id"`
|
||||||
InChannelId string `json:"in_channel_id"`
|
InChannelId string `json:"in_channel_id"`
|
||||||
NotInChannelId string `json:"not_in_channel_id"`
|
NotInChannelId string `json:"not_in_channel_id"`
|
||||||
InGroupId string `json:"in_group_id"`
|
InGroupId string `json:"in_group_id"`
|
||||||
GroupConstrained bool `json:"group_constrained"`
|
GroupConstrained bool `json:"group_constrained"`
|
||||||
AllowInactive bool `json:"allow_inactive"`
|
AllowInactive bool `json:"allow_inactive"`
|
||||||
WithoutTeam bool `json:"without_team"`
|
WithoutTeam bool `json:"without_team"`
|
||||||
Limit int `json:"limit"`
|
Limit int `json:"limit"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
|
ChannelRoles []string `json:"channel_roles"`
|
||||||
|
TeamRoles []string `json:"team_roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToJson convert a User to a json string
|
// ToJson convert a User to a json string
|
||||||
@@ -61,6 +64,12 @@ type UserSearchOptions struct {
|
|||||||
Limit int
|
Limit int
|
||||||
// Filters for the given role
|
// Filters for the given role
|
||||||
Role string
|
Role string
|
||||||
|
// Filters for users that have any of the given system roles
|
||||||
|
Roles []string
|
||||||
|
// Filters for users that have the given channel roles to be used when searching in a channel
|
||||||
|
ChannelRoles []string
|
||||||
|
// Filters for users that have the given team roles to be used when searching in a team
|
||||||
|
TeamRoles []string
|
||||||
// Restrict to search in a list of teams and channels
|
// Restrict to search in a list of teams and channels
|
||||||
ViewRestrictions *ViewUsersRestrictions
|
ViewRestrictions *ViewUsersRestrictions
|
||||||
// List of allowed channels
|
// List of allowed channels
|
||||||
|
|||||||
@@ -458,6 +458,121 @@ func applyRoleFilter(query sq.SelectBuilder, role string, isPostgreSQL bool) sq.
|
|||||||
return query.Where("u.Roles LIKE ? ESCAPE '*'", roleParam)
|
return query.Where("u.Roles LIKE ? ESCAPE '*'", roleParam)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applyMultiRoleFilters(query sq.SelectBuilder, roles []string, teamRoles []string, channelRoles []string) sq.SelectBuilder {
|
||||||
|
queryString := ""
|
||||||
|
if len(roles) > 0 && roles[0] != "" {
|
||||||
|
schemeGuest := false
|
||||||
|
schemeAdmin := false
|
||||||
|
schemeUser := false
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
switch role {
|
||||||
|
case model.SYSTEM_ADMIN_ROLE_ID:
|
||||||
|
schemeAdmin = true
|
||||||
|
case model.SYSTEM_USER_ROLE_ID:
|
||||||
|
schemeUser = true
|
||||||
|
case model.SYSTEM_GUEST_ROLE_ID:
|
||||||
|
schemeGuest = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeAdmin || schemeUser || schemeGuest {
|
||||||
|
if schemeAdmin && schemeUser {
|
||||||
|
queryString += `(u.Roles LIKE '%system_user%' OR u.Roles LIKE '%system_admin%') `
|
||||||
|
} else if schemeAdmin {
|
||||||
|
queryString += `(u.Roles LIKE '%system_admin%') `
|
||||||
|
} else if schemeUser {
|
||||||
|
queryString += `(u.Roles LIKE '%system_user%' AND u.Roles NOT LIKE '%system_admin%') `
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeGuest {
|
||||||
|
if queryString != "" {
|
||||||
|
queryString += "OR "
|
||||||
|
}
|
||||||
|
queryString += `(u.Roles LIKE '%system_guest%') `
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(channelRoles) > 0 && channelRoles[0] != "" {
|
||||||
|
schemeGuest := false
|
||||||
|
schemeAdmin := false
|
||||||
|
schemeUser := false
|
||||||
|
for _, channelRole := range channelRoles {
|
||||||
|
switch channelRole {
|
||||||
|
case model.CHANNEL_ADMIN_ROLE_ID:
|
||||||
|
schemeAdmin = true
|
||||||
|
case model.CHANNEL_USER_ROLE_ID:
|
||||||
|
schemeUser = true
|
||||||
|
case model.CHANNEL_GUEST_ROLE_ID:
|
||||||
|
schemeGuest = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeAdmin || schemeUser || schemeGuest {
|
||||||
|
if queryString != "" {
|
||||||
|
queryString += "OR "
|
||||||
|
}
|
||||||
|
if schemeAdmin && schemeUser {
|
||||||
|
queryString += `(cm.SchemeUser = true AND u.Roles = 'system_user')`
|
||||||
|
} else if schemeAdmin {
|
||||||
|
queryString += `(cm.SchemeAdmin = true AND u.Roles = 'system_user')`
|
||||||
|
} else if schemeUser {
|
||||||
|
queryString += `(cm.SchemeUser = true AND cm.SchemeAdmin = false AND u.Roles = 'system_user')`
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeGuest {
|
||||||
|
if queryString != "" && queryString[len(queryString)-3:] != "OR " {
|
||||||
|
queryString += "OR "
|
||||||
|
}
|
||||||
|
queryString += `(cm.SchemeGuest = true AND u.Roles = 'system_guest')`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(teamRoles) > 0 && teamRoles[0] != "" {
|
||||||
|
schemeAdmin := false
|
||||||
|
schemeUser := false
|
||||||
|
schemeGuest := false
|
||||||
|
for _, teamRole := range teamRoles {
|
||||||
|
switch teamRole {
|
||||||
|
case model.TEAM_ADMIN_ROLE_ID:
|
||||||
|
schemeAdmin = true
|
||||||
|
case model.TEAM_USER_ROLE_ID:
|
||||||
|
schemeUser = true
|
||||||
|
case model.TEAM_GUEST_ROLE_ID:
|
||||||
|
schemeGuest = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeAdmin || schemeUser || schemeGuest {
|
||||||
|
if queryString != "" {
|
||||||
|
queryString += "OR "
|
||||||
|
}
|
||||||
|
if schemeAdmin && schemeUser {
|
||||||
|
queryString += `(tm.SchemeUser = true AND u.Roles = 'system_user')`
|
||||||
|
} else if schemeAdmin {
|
||||||
|
queryString += `(tm.SchemeAdmin = true AND u.Roles = 'system_user')`
|
||||||
|
} else if schemeUser {
|
||||||
|
queryString += `(tm.SchemeUser = true AND tm.SchemeAdmin = false AND u.Roles = 'system_user')`
|
||||||
|
}
|
||||||
|
|
||||||
|
if schemeGuest {
|
||||||
|
if queryString != "" && queryString[len(queryString)-3:] != "OR " {
|
||||||
|
queryString += "OR "
|
||||||
|
}
|
||||||
|
queryString += `(tm.SchemeGuest = true AND u.Roles = 'system_guest')`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if queryString != "" {
|
||||||
|
query = query.Where("(" + queryString + ")")
|
||||||
|
}
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
func applyChannelGroupConstrainedFilter(query sq.SelectBuilder, channelId string) sq.SelectBuilder {
|
func applyChannelGroupConstrainedFilter(query sq.SelectBuilder, channelId string) sq.SelectBuilder {
|
||||||
if channelId == "" {
|
if channelId == "" {
|
||||||
return query
|
return query
|
||||||
@@ -525,6 +640,7 @@ func (us SqlUserStore) GetProfiles(options *model.UserGetOptions) ([]*model.User
|
|||||||
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
|
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
|
||||||
|
|
||||||
query = applyRoleFilter(query, options.Role, isPostgreSQL)
|
query = applyRoleFilter(query, options.Role, isPostgreSQL)
|
||||||
|
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
|
||||||
|
|
||||||
if options.Inactive {
|
if options.Inactive {
|
||||||
query = query.Where("u.DeleteAt != 0")
|
query = query.Where("u.DeleteAt != 0")
|
||||||
@@ -1079,6 +1195,7 @@ func (us SqlUserStore) PermanentDelete(userId string) *model.AppError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (us SqlUserStore) Count(options model.UserCountOptions) (int64, *model.AppError) {
|
func (us SqlUserStore) Count(options model.UserCountOptions) (int64, *model.AppError) {
|
||||||
|
isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
|
||||||
query := us.getQueryBuilder().Select("COUNT(DISTINCT u.Id)").From("Users AS u")
|
query := us.getQueryBuilder().Select("COUNT(DISTINCT u.Id)").From("Users AS u")
|
||||||
|
|
||||||
if !options.IncludeDeleted {
|
if !options.IncludeDeleted {
|
||||||
@@ -1099,10 +1216,13 @@ func (us SqlUserStore) Count(options model.UserCountOptions) (int64, *model.AppE
|
|||||||
|
|
||||||
if options.TeamId != "" {
|
if options.TeamId != "" {
|
||||||
query = query.LeftJoin("TeamMembers AS tm ON u.Id = tm.UserId").Where("tm.TeamId = ? AND tm.DeleteAt = 0", options.TeamId)
|
query = query.LeftJoin("TeamMembers AS tm ON u.Id = tm.UserId").Where("tm.TeamId = ? AND tm.DeleteAt = 0", options.TeamId)
|
||||||
|
} else if options.ChannelId != "" {
|
||||||
|
query = query.LeftJoin("ChannelMembers AS cm ON u.Id = cm.UserId").Where("cm.ChannelId = ?", options.ChannelId)
|
||||||
}
|
}
|
||||||
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, false)
|
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, false)
|
||||||
|
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
|
||||||
|
|
||||||
if us.DriverName() == model.DATABASE_DRIVER_POSTGRES {
|
if isPostgreSQL {
|
||||||
query = query.PlaceholderFormat(sq.Dollar)
|
query = query.PlaceholderFormat(sq.Dollar)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1309,6 +1429,7 @@ func (us SqlUserStore) performSearch(query sq.SelectBuilder, term string, option
|
|||||||
isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
|
isPostgreSQL := us.DriverName() == model.DATABASE_DRIVER_POSTGRES
|
||||||
|
|
||||||
query = applyRoleFilter(query, options.Role, isPostgreSQL)
|
query = applyRoleFilter(query, options.Role, isPostgreSQL)
|
||||||
|
query = applyMultiRoleFilters(query, options.Roles, options.TeamRoles, options.ChannelRoles)
|
||||||
|
|
||||||
if !options.AllowInactive {
|
if !options.AllowInactive {
|
||||||
query = query.Where("u.DeleteAt = 0")
|
query = query.Where("u.DeleteAt = 0")
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ func TestUserStore(t *testing.T, ss store.Store, s SqlSupplier) {
|
|||||||
t.Run("UpdateMfaActive", func(t *testing.T) { testUserStoreUpdateMfaActive(t, ss) })
|
t.Run("UpdateMfaActive", func(t *testing.T) { testUserStoreUpdateMfaActive(t, ss) })
|
||||||
t.Run("GetRecentlyActiveUsersForTeam", func(t *testing.T) { testUserStoreGetRecentlyActiveUsersForTeam(t, ss, s) })
|
t.Run("GetRecentlyActiveUsersForTeam", func(t *testing.T) { testUserStoreGetRecentlyActiveUsersForTeam(t, ss, s) })
|
||||||
t.Run("GetNewUsersForTeam", func(t *testing.T) { testUserStoreGetNewUsersForTeam(t, ss) })
|
t.Run("GetNewUsersForTeam", func(t *testing.T) { testUserStoreGetNewUsersForTeam(t, ss) })
|
||||||
|
t.Run("Search", func(t *testing.T) { testUserStoreSearch(t, ss) })
|
||||||
t.Run("SearchNotInChannel", func(t *testing.T) { testUserStoreSearchNotInChannel(t, ss) })
|
t.Run("SearchNotInChannel", func(t *testing.T) { testUserStoreSearchNotInChannel(t, ss) })
|
||||||
t.Run("SearchInChannel", func(t *testing.T) { testUserStoreSearchInChannel(t, ss) })
|
t.Run("SearchInChannel", func(t *testing.T) { testUserStoreSearchInChannel(t, ss) })
|
||||||
t.Run("SearchNotInTeam", func(t *testing.T) { testUserStoreSearchNotInTeam(t, ss) })
|
t.Run("SearchNotInTeam", func(t *testing.T) { testUserStoreSearchNotInTeam(t, ss) })
|
||||||
@@ -2291,6 +2292,153 @@ func assertUsers(t *testing.T, expected, actual []*model.User) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testUserStoreSearch(t *testing.T, ss store.Store) {
|
||||||
|
u1 := &model.User{
|
||||||
|
Username: "jimbo1" + model.NewId(),
|
||||||
|
FirstName: "Tim",
|
||||||
|
LastName: "Bill",
|
||||||
|
Nickname: "Rob",
|
||||||
|
Email: "harold" + model.NewId() + "@simulator.amazonses.com",
|
||||||
|
Roles: "system_user system_admin",
|
||||||
|
}
|
||||||
|
_, err := ss.User().Save(u1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
|
||||||
|
|
||||||
|
u2 := &model.User{
|
||||||
|
Username: "jim2-bobby" + model.NewId(),
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Roles: "system_user",
|
||||||
|
}
|
||||||
|
_, err = ss.User().Save(u2)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
|
||||||
|
|
||||||
|
u3 := &model.User{
|
||||||
|
Username: "jimbo3" + model.NewId(),
|
||||||
|
Email: MakeEmail(),
|
||||||
|
Roles: "system_guest",
|
||||||
|
}
|
||||||
|
_, err = ss.User().Save(u3)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(u3.Id)) }()
|
||||||
|
|
||||||
|
// The users returned from the database will have AuthData as an empty string.
|
||||||
|
nilAuthData := new(string)
|
||||||
|
*nilAuthData = ""
|
||||||
|
u1.AuthData = nilAuthData
|
||||||
|
u2.AuthData = nilAuthData
|
||||||
|
u3.AuthData = nilAuthData
|
||||||
|
|
||||||
|
t1id := model.NewId()
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: t1id, UserId: u1.Id, SchemeAdmin: true, SchemeUser: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: t1id, UserId: u2.Id, SchemeAdmin: true, SchemeUser: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: t1id, UserId: u3.Id, SchemeAdmin: false, SchemeUser: false, SchemeGuest: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
Description string
|
||||||
|
TeamId string
|
||||||
|
Term string
|
||||||
|
Options *model.UserSearchOptions
|
||||||
|
Expected []*model.User
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"search jimb, team 1",
|
||||||
|
t1id,
|
||||||
|
"jimb",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
},
|
||||||
|
[]*model.User{u1, u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jimb, team 1 with team guest and team admin filters without sys admin filter",
|
||||||
|
t1id,
|
||||||
|
"jimb",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
TeamRoles: []string{model.TEAM_GUEST_ROLE_ID, model.TEAM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jimb, team 1 with team admin filter and sys admin filter",
|
||||||
|
t1id,
|
||||||
|
"jimb",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
TeamRoles: []string{model.TEAM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, team 1 with team admin filter",
|
||||||
|
t1id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
TeamRoles: []string{model.TEAM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u2},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, team 1 with team admin and team guest filter",
|
||||||
|
t1id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
TeamRoles: []string{model.TEAM_ADMIN_ROLE_ID, model.TEAM_GUEST_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u2, u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, team 1 with team admin and system admin filters",
|
||||||
|
t1id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
TeamRoles: []string{model.TEAM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u2, u1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, team 1 with system guest filter",
|
||||||
|
t1id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
Roles: []string{model.SYSTEM_GUEST_ROLE_ID},
|
||||||
|
TeamRoles: []string{},
|
||||||
|
},
|
||||||
|
[]*model.User{u3},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Description, func(t *testing.T) {
|
||||||
|
users, err := ss.User().Search(
|
||||||
|
testCase.TeamId,
|
||||||
|
testCase.Term,
|
||||||
|
testCase.Options,
|
||||||
|
)
|
||||||
|
require.Nil(t, err)
|
||||||
|
assertUsers(t, testCase.Expected, users)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testUserStoreSearchNotInChannel(t *testing.T, ss store.Store) {
|
func testUserStoreSearchNotInChannel(t *testing.T, ss store.Store) {
|
||||||
u1 := &model.User{
|
u1 := &model.User{
|
||||||
Username: "jimbo1" + model.NewId(),
|
Username: "jimbo1" + model.NewId(),
|
||||||
@@ -2524,6 +2672,7 @@ func testUserStoreSearchInChannel(t *testing.T, ss store.Store) {
|
|||||||
LastName: "Bill",
|
LastName: "Bill",
|
||||||
Nickname: "Rob",
|
Nickname: "Rob",
|
||||||
Email: "harold" + model.NewId() + "@simulator.amazonses.com",
|
Email: "harold" + model.NewId() + "@simulator.amazonses.com",
|
||||||
|
Roles: "system_user system_admin",
|
||||||
}
|
}
|
||||||
_, err := ss.User().Save(u1)
|
_, err := ss.User().Save(u1)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -2532,6 +2681,7 @@ func testUserStoreSearchInChannel(t *testing.T, ss store.Store) {
|
|||||||
u2 := &model.User{
|
u2 := &model.User{
|
||||||
Username: "jim-bobby" + model.NewId(),
|
Username: "jim-bobby" + model.NewId(),
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
|
Roles: "system_user",
|
||||||
}
|
}
|
||||||
_, err = ss.User().Save(u2)
|
_, err = ss.User().Save(u2)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -2541,6 +2691,7 @@ func testUserStoreSearchInChannel(t *testing.T, ss store.Store) {
|
|||||||
Username: "jimbo3" + model.NewId(),
|
Username: "jimbo3" + model.NewId(),
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
DeleteAt: 1,
|
DeleteAt: 1,
|
||||||
|
Roles: "system_user",
|
||||||
}
|
}
|
||||||
_, err = ss.User().Save(u3)
|
_, err = ss.User().Save(u3)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
@@ -2592,18 +2743,24 @@ func testUserStoreSearchInChannel(t *testing.T, ss store.Store) {
|
|||||||
ChannelId: c1.Id,
|
ChannelId: c1.Id,
|
||||||
UserId: u1.Id,
|
UserId: u1.Id,
|
||||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
SchemeAdmin: true,
|
||||||
|
SchemeUser: true,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{
|
||||||
ChannelId: c2.Id,
|
ChannelId: c2.Id,
|
||||||
UserId: u2.Id,
|
UserId: u2.Id,
|
||||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
SchemeAdmin: false,
|
||||||
|
SchemeUser: true,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
_, err = ss.Channel().SaveMember(&model.ChannelMember{
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{
|
||||||
ChannelId: c1.Id,
|
ChannelId: c1.Id,
|
||||||
UserId: u3.Id,
|
UserId: u3.Id,
|
||||||
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
||||||
|
SchemeAdmin: false,
|
||||||
|
SchemeUser: true,
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
@@ -2667,6 +2824,66 @@ func testUserStoreSearchInChannel(t *testing.T, ss store.Store) {
|
|||||||
},
|
},
|
||||||
[]*model.User{},
|
[]*model.User{},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"search jim, allow inactive, channel 1 with system admin filter",
|
||||||
|
c1.Id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
AllowInactive: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u1},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, allow inactive, channel 1 with system admin and system user filter",
|
||||||
|
c1.Id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
AllowInactive: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID, model.SYSTEM_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u1, u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, allow inactive, channel 1 with channel user filter",
|
||||||
|
c1.Id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
AllowInactive: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, allow inactive, channel 1 with channel user and channel admin filter",
|
||||||
|
c1.Id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
AllowInactive: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"search jim, allow inactive, channel 2 with channel user filter",
|
||||||
|
c2.Id,
|
||||||
|
"jim",
|
||||||
|
&model.UserSearchOptions{
|
||||||
|
AllowFullNames: true,
|
||||||
|
AllowInactive: true,
|
||||||
|
Limit: model.USER_SEARCH_DEFAULT_LIMIT,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
[]*model.User{u2},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
@@ -3122,111 +3339,287 @@ func testUserStoreSearchInGroup(t *testing.T, ss store.Store) {
|
|||||||
func testCount(t *testing.T, ss store.Store) {
|
func testCount(t *testing.T, ss store.Store) {
|
||||||
// Regular
|
// Regular
|
||||||
teamId := model.NewId()
|
teamId := model.NewId()
|
||||||
u1 := &model.User{}
|
channelId := model.NewId()
|
||||||
u1.Email = MakeEmail()
|
regularUser := &model.User{}
|
||||||
_, err := ss.User().Save(u1)
|
regularUser.Email = MakeEmail()
|
||||||
|
regularUser.Roles = model.SYSTEM_USER_ROLE_ID
|
||||||
|
_, err := ss.User().Save(regularUser)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
defer func() { require.Nil(t, ss.User().PermanentDelete(u1.Id)) }()
|
defer func() { require.Nil(t, ss.User().PermanentDelete(regularUser.Id)) }()
|
||||||
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: u1.Id}, -1)
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: regularUser.Id, SchemeAdmin: false, SchemeUser: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{UserId: regularUser.Id, ChannelId: channelId, SchemeAdmin: false, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
guestUser := &model.User{}
|
||||||
|
guestUser.Email = MakeEmail()
|
||||||
|
guestUser.Roles = model.SYSTEM_GUEST_ROLE_ID
|
||||||
|
_, err = ss.User().Save(guestUser)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(guestUser.Id)) }()
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: guestUser.Id, SchemeAdmin: false, SchemeUser: false, SchemeGuest: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{UserId: guestUser.Id, ChannelId: channelId, SchemeAdmin: false, SchemeUser: false, SchemeGuest: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
teamAdmin := &model.User{}
|
||||||
|
teamAdmin.Email = MakeEmail()
|
||||||
|
teamAdmin.Roles = model.SYSTEM_USER_ROLE_ID
|
||||||
|
_, err = ss.User().Save(teamAdmin)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(teamAdmin.Id)) }()
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: teamAdmin.Id, SchemeAdmin: true, SchemeUser: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{UserId: teamAdmin.Id, ChannelId: channelId, SchemeAdmin: true, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
sysAdmin := &model.User{}
|
||||||
|
sysAdmin.Email = MakeEmail()
|
||||||
|
sysAdmin.Roles = model.SYSTEM_ADMIN_ROLE_ID + " " + model.SYSTEM_USER_ROLE_ID
|
||||||
|
_, err = ss.User().Save(sysAdmin)
|
||||||
|
require.Nil(t, err)
|
||||||
|
defer func() { require.Nil(t, ss.User().PermanentDelete(sysAdmin.Id)) }()
|
||||||
|
_, err = ss.Team().SaveMember(&model.TeamMember{TeamId: teamId, UserId: sysAdmin.Id, SchemeAdmin: false, SchemeUser: true}, -1)
|
||||||
|
require.Nil(t, err)
|
||||||
|
_, err = ss.Channel().SaveMember(&model.ChannelMember{UserId: sysAdmin.Id, ChannelId: channelId, SchemeAdmin: true, SchemeUser: true, NotifyProps: model.GetDefaultChannelNotifyProps()})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
// Deleted
|
// Deleted
|
||||||
u2 := &model.User{}
|
deletedUser := &model.User{}
|
||||||
u2.Email = MakeEmail()
|
deletedUser.Email = MakeEmail()
|
||||||
u2.DeleteAt = model.GetMillis()
|
deletedUser.DeleteAt = model.GetMillis()
|
||||||
_, err = ss.User().Save(u2)
|
_, err = ss.User().Save(deletedUser)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
defer func() { require.Nil(t, ss.User().PermanentDelete(u2.Id)) }()
|
defer func() { require.Nil(t, ss.User().PermanentDelete(deletedUser.Id)) }()
|
||||||
|
|
||||||
// Bot
|
// Bot
|
||||||
u3, err := ss.User().Save(&model.User{
|
botUser, err := ss.User().Save(&model.User{
|
||||||
Email: MakeEmail(),
|
Email: MakeEmail(),
|
||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
defer func() { require.Nil(t, ss.User().PermanentDelete(u3.Id)) }()
|
defer func() { require.Nil(t, ss.User().PermanentDelete(botUser.Id)) }()
|
||||||
_, nErr := ss.Bot().Save(&model.Bot{
|
_, nErr := ss.Bot().Save(&model.Bot{
|
||||||
UserId: u3.Id,
|
UserId: botUser.Id,
|
||||||
Username: u3.Username,
|
Username: botUser.Username,
|
||||||
OwnerId: u1.Id,
|
OwnerId: regularUser.Id,
|
||||||
})
|
})
|
||||||
require.Nil(t, nErr)
|
require.Nil(t, nErr)
|
||||||
u3.IsBot = true
|
botUser.IsBot = true
|
||||||
defer func() { require.Nil(t, ss.Bot().PermanentDelete(u3.Id)) }()
|
defer func() { require.Nil(t, ss.Bot().PermanentDelete(botUser.Id)) }()
|
||||||
|
|
||||||
count, err := ss.User().Count(model.UserCountOptions{
|
testCases := []struct {
|
||||||
IncludeBotAccounts: false,
|
Description string
|
||||||
IncludeDeleted: false,
|
Options model.UserCountOptions
|
||||||
TeamId: "",
|
Expected int64
|
||||||
})
|
}{
|
||||||
require.Nil(t, err)
|
{
|
||||||
require.Equal(t, int64(1), count)
|
"No bot accounts no deleted accounts and no team id",
|
||||||
|
model.UserCountOptions{
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
IncludeBotAccounts: false,
|
||||||
IncludeBotAccounts: true,
|
IncludeDeleted: false,
|
||||||
IncludeDeleted: false,
|
TeamId: "",
|
||||||
TeamId: "",
|
},
|
||||||
})
|
4,
|
||||||
require.Nil(t, err)
|
},
|
||||||
require.Equal(t, int64(2), count)
|
{
|
||||||
|
"Include bot accounts no deleted accounts and no team id",
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
model.UserCountOptions{
|
||||||
IncludeBotAccounts: false,
|
IncludeBotAccounts: true,
|
||||||
IncludeDeleted: true,
|
IncludeDeleted: false,
|
||||||
TeamId: "",
|
TeamId: "",
|
||||||
})
|
},
|
||||||
require.Nil(t, err)
|
5,
|
||||||
require.Equal(t, int64(2), count)
|
},
|
||||||
|
{
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
"Include delete accounts no bots and no team id",
|
||||||
IncludeBotAccounts: true,
|
model.UserCountOptions{
|
||||||
IncludeDeleted: true,
|
IncludeBotAccounts: false,
|
||||||
TeamId: "",
|
IncludeDeleted: true,
|
||||||
})
|
TeamId: "",
|
||||||
require.Nil(t, err)
|
},
|
||||||
require.Equal(t, int64(3), count)
|
5,
|
||||||
|
},
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
{
|
||||||
IncludeBotAccounts: true,
|
"Include bot accounts and deleted accounts and no team id",
|
||||||
IncludeDeleted: true,
|
model.UserCountOptions{
|
||||||
ExcludeRegularUsers: true,
|
IncludeBotAccounts: true,
|
||||||
TeamId: "",
|
IncludeDeleted: true,
|
||||||
})
|
TeamId: "",
|
||||||
require.Nil(t, err)
|
},
|
||||||
require.Equal(t, int64(1), count)
|
6,
|
||||||
|
},
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
{
|
||||||
IncludeBotAccounts: true,
|
"Include bot accounts, deleted accounts, exclude regular users with no team id",
|
||||||
IncludeDeleted: true,
|
model.UserCountOptions{
|
||||||
TeamId: teamId,
|
IncludeBotAccounts: true,
|
||||||
})
|
IncludeDeleted: true,
|
||||||
require.Nil(t, err)
|
ExcludeRegularUsers: true,
|
||||||
require.Equal(t, int64(1), count)
|
TeamId: "",
|
||||||
|
},
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
1,
|
||||||
IncludeBotAccounts: true,
|
},
|
||||||
IncludeDeleted: true,
|
{
|
||||||
TeamId: model.NewId(),
|
"Include bot accounts and deleted accounts with existing team id",
|
||||||
})
|
model.UserCountOptions{
|
||||||
require.Nil(t, err)
|
IncludeBotAccounts: true,
|
||||||
require.Equal(t, int64(0), count)
|
IncludeDeleted: true,
|
||||||
|
TeamId: teamId,
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
},
|
||||||
IncludeBotAccounts: true,
|
4,
|
||||||
IncludeDeleted: true,
|
},
|
||||||
TeamId: teamId,
|
{
|
||||||
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{teamId}},
|
"Include bot accounts and deleted accounts with fake team id",
|
||||||
})
|
model.UserCountOptions{
|
||||||
require.Nil(t, err)
|
IncludeBotAccounts: true,
|
||||||
require.Equal(t, int64(1), count)
|
IncludeDeleted: true,
|
||||||
|
TeamId: model.NewId(),
|
||||||
count, err = ss.User().Count(model.UserCountOptions{
|
},
|
||||||
IncludeBotAccounts: true,
|
0,
|
||||||
IncludeDeleted: true,
|
},
|
||||||
TeamId: teamId,
|
{
|
||||||
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{model.NewId()}},
|
"Include bot accounts and deleted accounts with existing team id and view restrictions allowing team",
|
||||||
})
|
model.UserCountOptions{
|
||||||
require.Nil(t, err)
|
IncludeBotAccounts: true,
|
||||||
require.Equal(t, int64(0), count)
|
IncludeDeleted: true,
|
||||||
|
TeamId: teamId,
|
||||||
|
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{teamId}},
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Include bot accounts and deleted accounts with existing team id and view restrictions not allowing current team",
|
||||||
|
model.UserCountOptions{
|
||||||
|
IncludeBotAccounts: true,
|
||||||
|
IncludeDeleted: true,
|
||||||
|
TeamId: teamId,
|
||||||
|
ViewRestrictions: &model.ViewUsersRestrictions{Teams: []string{model.NewId()}},
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by system admins only",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by system users only",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
Roles: []string{model.SYSTEM_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by system guests only",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
Roles: []string{model.SYSTEM_GUEST_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by system admins and system users",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID, model.SYSTEM_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by system admins, system user and system guests",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID, model.SYSTEM_USER_ROLE_ID, model.SYSTEM_GUEST_ROLE_ID},
|
||||||
|
},
|
||||||
|
4,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by team admins",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
TeamRoles: []string{model.TEAM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by team members",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
TeamRoles: []string{model.TEAM_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by team guests",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
TeamRoles: []string{model.TEAM_GUEST_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by team guests and any system role",
|
||||||
|
model.UserCountOptions{
|
||||||
|
TeamId: teamId,
|
||||||
|
TeamRoles: []string{model.TEAM_GUEST_ROLE_ID},
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by channel members",
|
||||||
|
model.UserCountOptions{
|
||||||
|
ChannelId: channelId,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by channel members and system admins",
|
||||||
|
model.UserCountOptions{
|
||||||
|
ChannelId: channelId,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID},
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by channel members and system admins and channel admins",
|
||||||
|
model.UserCountOptions{
|
||||||
|
ChannelId: channelId,
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
ChannelRoles: []string{model.CHANNEL_USER_ROLE_ID, model.CHANNEL_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by channel guests",
|
||||||
|
model.UserCountOptions{
|
||||||
|
ChannelId: channelId,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_GUEST_ROLE_ID},
|
||||||
|
},
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Filter by channel guests and any system role",
|
||||||
|
model.UserCountOptions{
|
||||||
|
ChannelId: channelId,
|
||||||
|
ChannelRoles: []string{model.CHANNEL_GUEST_ROLE_ID},
|
||||||
|
Roles: []string{model.SYSTEM_ADMIN_ROLE_ID},
|
||||||
|
},
|
||||||
|
2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.Description, func(t *testing.T) {
|
||||||
|
count, err := ss.User().Count(testCase.Options)
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Equal(t, testCase.Expected, count)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testUserStoreAnalyticsActiveCount(t *testing.T, ss store.Store, s SqlSupplier) {
|
func testUserStoreAnalyticsActiveCount(t *testing.T, ss store.Store, s SqlSupplier) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user