Adding new "VIEW_MEMBERS" permissions restrict the scope of users visibility (#10487)

* MM-14138: Adding new "VIEW_MEMBERS" permissions restrict the scope of users visibility

* Fixing gofmt

* Fixing broken tests

* Addressing PR review comments from Miguel de la Cruz

* Removed hack

* A bit nicer and cleaner code in the UserBelongsToChannels function

* Adding cluster cache invalidation for user team ids

* Checking in the correct order permissions to not leek existency information

* Adding restrictions to TeamMembers and User status requests

* Fixing tests

* Fixing status endpoint permissions checks

* Adding more tests

* Fixing tests

* More tests and making the restrictions query based only on joins

* Adding more tests

* Adding more tests

* fixing merge problems

* Reverting status changes to avoid performance issues

* Adding more tests

* Fixing test

* i18n extract

* Adding extra method for get restrictions for a team

* Add the new elasticsearch functions to search users with restrictions

* Add missing translation string

* Rename restrictedChannelIds to restrictedToChannels

* Remove ToDo

* Adding the permission to the SystemAdmin role during permissions migrations
Этот коммит содержится в:
Jesús Espino
2019-04-29 16:56:56 +02:00
коммит произвёл GitHub
родитель 5b70962f71
Коммит c8920588a0
41 изменённых файлов: 2785 добавлений и 259 удалений

Просмотреть файл

@@ -2604,3 +2604,27 @@ func (s SqlChannelStore) GetChannelsBatchForIndexing(startTime, endTime int64, l
result.Data = channels
})
}
func (s SqlChannelStore) UserBelongsToChannels(userId string, channelIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := s.getQueryBuilder().
Select("Count(*)").
From("ChannelMembers").
Where(sq.And{
sq.Eq{"UserId": userId},
sq.Eq{"ChannelId": channelIds},
})
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
c, err := s.GetReplica().SelectInt(queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.UserBelongsToChannels", "store.sql_channel.user_belongs_to_channels.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
result.Data = c > 0
})
}

Просмотреть файл

@@ -124,7 +124,7 @@ func NewSqlSupplier(settings model.SqlSettings, metrics einterfaces.MetricsInter
supplier.initConnection()
supplier.oldStores.team = NewSqlTeamStore(supplier)
supplier.oldStores.team = NewSqlTeamStore(supplier, metrics)
supplier.oldStores.channel = NewSqlChannelStore(supplier, metrics)
supplier.oldStores.post = NewSqlPostStore(supplier, metrics)
supplier.oldStores.user = NewSqlUserStore(supplier, metrics)

Просмотреть файл

@@ -5,21 +5,28 @@ package sqlstore
import (
"database/sql"
"fmt"
"net/http"
"strconv"
"strings"
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/gorp"
"github.com/mattermost/mattermost-server/einterfaces"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
"github.com/mattermost/mattermost-server/utils"
)
const (
TEAM_MEMBER_EXISTS_ERROR = "store.sql_team.save_member.exists.app_error"
TEAM_MEMBER_EXISTS_ERROR = "store.sql_team.save_member.exists.app_error"
ALL_TEAM_IDS_FOR_USER_CACHE_SIZE = model.SESSION_CACHE_SIZE
ALL_TEAM_IDS_FOR_USER_CACHE_SEC = 1800 // 30 mins
)
type SqlTeamStore struct {
SqlStore
metrics einterfaces.MetricsInterface
}
type teamMember struct {
@@ -132,8 +139,11 @@ func (db teamMemberWithSchemeRolesList) ToModel() []*model.TeamMember {
return tms
}
func NewSqlTeamStore(sqlStore SqlStore) store.TeamStore {
s := &SqlTeamStore{sqlStore}
func NewSqlTeamStore(sqlStore SqlStore, metrics einterfaces.MetricsInterface) store.TeamStore {
s := &SqlTeamStore{
sqlStore,
metrics,
}
for _, db := range sqlStore.GetAllConns() {
table := db.AddTableWithName(model.Team{}, "Teams").SetKeys(false, "Id")
@@ -224,6 +234,11 @@ func (s SqlTeamStore) Update(team *model.Team) (*model.Team, *model.AppError) {
return nil, model.NewAppError("SqlTeamStore.Update", "store.sql_team.update.app_error", nil, "id="+team.Id, http.StatusInternalServerError)
}
if oldTeam.DeleteAt == 0 && team.DeleteAt != 0 {
// Invalidate this cache after any team deletion
allTeamIdsForUserCache.Purge()
}
return team, nil
}
@@ -461,21 +476,21 @@ func (s SqlTeamStore) AnalyticsTeamCount() store.StoreChannel {
})
}
var TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY = `
SELECT
TeamMembers.*,
TeamScheme.DefaultTeamUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultTeamAdminRole TeamSchemeDefaultAdminRole
FROM
TeamMembers
LEFT JOIN
Teams ON TeamMembers.TeamId = Teams.Id
LEFT JOIN
Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id
`
func (s SqlTeamStore) getTeamMembersWithSchemeSelectQuery() sq.SelectBuilder {
return s.getQueryBuilder().
Select(
"TeamMembers.*",
"TeamScheme.DefaultTeamUserRole TeamSchemeDefaultUserRole",
"TeamScheme.DefaultTeamAdminRole TeamSchemeDefaultAdminRole",
).
From("TeamMembers").
LeftJoin("Teams ON TeamMembers.TeamId = Teams.Id").
LeftJoin("Schemes TeamScheme ON Teams.SchemeId = TeamScheme.Id")
}
func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
defer s.InvalidateAllTeamIdsForUser(member.UserId)
if result.Err = member.IsValid(); result.Err != nil {
return
}
@@ -517,8 +532,18 @@ func (s SqlTeamStore) SaveMember(member *model.TeamMember, maxUsersPerTeam int)
return
}
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.TeamId": dbMember.TeamId}).
Where(sq.Eq{"TeamMembers.UserId": dbMember.UserId})
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": dbMember.TeamId, "UserId": dbMember.UserId}); err != nil {
if err := s.GetMaster().SelectOne(&retrievedMember, queryString, args...); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.SaveMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+dbMember.TeamId+"user_id="+dbMember.UserId+","+err.Error(), http.StatusNotFound)
return
@@ -543,8 +568,18 @@ func (s SqlTeamStore) UpdateMember(member *model.TeamMember) store.StoreChannel
return
}
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.TeamId": member.TeamId}).
Where(sq.Eq{"TeamMembers.UserId": member.UserId})
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var retrievedMember teamMemberWithSchemeRoles
if err := s.GetMaster().SelectOne(&retrievedMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": member.TeamId, "UserId": member.UserId}); err != nil {
if err := s.GetMaster().SelectOne(&retrievedMember, queryString, args...); err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.UpdateMember", "store.sql_team.get_member.missing.app_error", nil, "team_id="+member.TeamId+"user_id="+member.UserId+","+err.Error(), http.StatusNotFound)
return
@@ -559,8 +594,18 @@ func (s SqlTeamStore) UpdateMember(member *model.TeamMember) store.StoreChannel
func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.TeamId": teamId}).
Where(sq.Eq{"TeamMembers.UserId": userId})
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var dbMember teamMemberWithSchemeRoles
err := s.GetReplica().SelectOne(&dbMember, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
err = s.GetReplica().SelectOne(&dbMember, queryString, args...)
if err != nil {
if err == sql.ErrNoRows {
result.Err = model.NewAppError("SqlTeamStore.GetMember", "store.sql_team.get_member.missing.app_error", nil, "teamId="+teamId+" userId="+userId+" "+err.Error(), http.StatusNotFound)
@@ -573,10 +618,24 @@ func (s SqlTeamStore) GetMember(teamId string, userId string) store.StoreChannel
})
}
func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int) store.StoreChannel {
func (s SqlTeamStore) GetMembers(teamId string, offset int, limit int, restrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.TeamId": teamId}).
Where(sq.Eq{"TeamMembers.DeleteAt": 0}).
Limit(uint64(limit)).
Offset(uint64(offset))
query = applyTeamMemberViewRestrictionsFilter(query, teamId, restrictions)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.DeleteAt = 0 LIMIT :Limit OFFSET :Offset", map[string]interface{}{"TeamId": teamId, "Limit": limit, "Offset": offset})
_, err = s.GetReplica().Select(&dbMembers, queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
return
@@ -629,24 +688,27 @@ func (s SqlTeamStore) GetActiveMemberCount(teamId string) store.StoreChannel {
})
}
func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.StoreChannel {
func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string, restrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var dbMembers teamMemberWithSchemeRolesList
props := make(map[string]interface{})
idQuery := ""
for index, userId := range userIds {
if len(idQuery) > 0 {
idQuery += ", "
}
props["userId"+strconv.Itoa(index)] = userId
idQuery += ":userId" + strconv.Itoa(index)
if len(userIds) == 0 {
result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, "Invalid list of user ids", http.StatusInternalServerError)
}
props["TeamId"] = teamId
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.TeamId": teamId}).
Where(sq.Eq{"TeamMembers.UserId": userIds}).
Where(sq.Eq{"TeamMembers.DeleteAt": 0})
if _, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.TeamId = :TeamId AND TeamMembers.UserId IN ("+idQuery+") AND TeamMembers.DeleteAt = 0", props); err != nil {
query = applyTeamMemberViewRestrictionsFilter(query, teamId, restrictions)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var dbMembers teamMemberWithSchemeRolesList
if _, err := s.GetReplica().Select(&dbMembers, queryString, args...); err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembersByIds", "store.sql_team.get_members_by_ids.app_error", nil, "teamId="+teamId+" "+err.Error(), http.StatusInternalServerError)
return
}
@@ -656,8 +718,17 @@ func (s SqlTeamStore) GetMembersByIds(teamId string, userIds []string) store.Sto
func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.UserId": userId})
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var dbMembers teamMemberWithSchemeRolesList
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId", map[string]interface{}{"UserId": userId})
_, err = s.GetReplica().Select(&dbMembers, queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetMembers", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
return
@@ -669,9 +740,19 @@ func (s SqlTeamStore) GetTeamsForUser(userId string) store.StoreChannel {
func (s SqlTeamStore) GetTeamsForUserWithPagination(userId string, page, perPage int) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := s.getTeamMembersWithSchemeSelectQuery().
Where(sq.Eq{"TeamMembers.UserId": userId}).
Limit(uint64(perPage)).
Offset(uint64(page * perPage))
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetTeamsForUserWithPagination", "store.sql_team.get_members.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
var dbMembers teamMemberWithSchemeRolesList
offset := page * perPage
_, err := s.GetReplica().Select(&dbMembers, TEAM_MEMBERS_WITH_SCHEME_SELECT_QUERY+"WHERE TeamMembers.UserId = :UserId Limit :Limit Offset :Offset", map[string]interface{}{"UserId": userId, "Limit": perPage, "Offset": offset})
_, err = s.GetReplica().Select(&dbMembers, queryString, args...)
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetTeamsForUserWithPagination", "store.sql_team.get_members.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
return
@@ -731,7 +812,7 @@ func (s SqlTeamStore) RemoveMember(teamId string, userId string) store.StoreChan
return store.Do(func(result *store.StoreResult) {
_, err := s.GetMaster().Exec("DELETE FROM TeamMembers WHERE TeamId = :TeamId AND UserId = :UserId", map[string]interface{}{"TeamId": teamId, "UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "team_id="+teamId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlTeamStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "team_id="+teamId+", user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
}
})
}
@@ -740,7 +821,7 @@ func (s SqlTeamStore) RemoveAllMembersByTeam(teamId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
_, err := s.GetMaster().Exec("DELETE FROM TeamMembers WHERE TeamId = :TeamId", map[string]interface{}{"TeamId": teamId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "team_id="+teamId+", "+err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlTeamStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "team_id="+teamId+", "+err.Error(), http.StatusInternalServerError)
}
})
}
@@ -749,7 +830,7 @@ func (s SqlTeamStore) RemoveAllMembersByUser(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
_, err := s.GetMaster().Exec("DELETE FROM TeamMembers WHERE UserId = :UserId", map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlChannelStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
result.Err = model.NewAppError("SqlTeamStore.RemoveMember", "store.sql_team.remove_member.app_error", nil, "user_id="+userId+", "+err.Error(), http.StatusInternalServerError)
}
})
}
@@ -849,6 +930,22 @@ func (s SqlTeamStore) ResetAllTeamSchemes() store.StoreChannel {
})
}
var allTeamIdsForUserCache = utils.NewLru(ALL_TEAM_IDS_FOR_USER_CACHE_SIZE)
func (s SqlTeamStore) ClearCaches() {
allTeamIdsForUserCache.Purge()
if s.metrics != nil {
s.metrics.IncrementMemCacheInvalidationCounter("All Team Ids for User - Purge")
}
}
func (s SqlTeamStore) InvalidateAllTeamIdsForUser(userId string) {
allTeamIdsForUserCache.Remove(userId)
if s.metrics != nil {
s.metrics.IncrementMemCacheInvalidationCounter("All Team Ids for User - Remove by UserId")
}
}
func (s SqlTeamStore) ClearAllCustomRoleAssignments() store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
builtInRoles := model.MakeDefaultRoles()
@@ -944,6 +1041,48 @@ func (s SqlTeamStore) GetAllForExportAfter(limit int, afterId string) store.Stor
})
}
func (s SqlTeamStore) GetUserTeamIds(userId string, allowFromCache bool) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
if allowFromCache {
if cacheItem, ok := allTeamIdsForUserCache.Get(userId); ok {
if s.metrics != nil {
s.metrics.IncrementMemCacheHitCounter("All Team Ids for User")
}
result.Data = cacheItem.([]string)
return
}
}
if s.metrics != nil {
s.metrics.IncrementMemCacheMissCounter("All Team Ids for User")
}
var teamIds []string
_, err := s.GetReplica().Select(&teamIds, `
SELECT
TeamId
FROM
TeamMembers
INNER JOIN
Teams ON TeamMembers.TeamId = Teams.Id
WHERE
TeamMembers.UserId = :UserId
AND TeamMembers.DeleteAt = 0
AND Teams.DeleteAt = 0`,
map[string]interface{}{"UserId": userId})
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.GetUserTeamIds", "store.sql_team.get_user_team_ids.app_error", nil, "userId="+userId+" "+err.Error(), http.StatusInternalServerError)
return
}
result.Data = teamIds
if allowFromCache {
allTeamIdsForUserCache.AddWithExpiresInSecs(userId, teamIds, ALL_TEAM_IDS_FOR_USER_CACHE_SEC)
}
})
}
func (s SqlTeamStore) GetTeamMembersForExport(userId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
var members []*model.TeamMemberForExport
@@ -967,3 +1106,56 @@ func (s SqlTeamStore) GetTeamMembersForExport(userId string) store.StoreChannel
result.Data = members
})
}
func (s SqlTeamStore) UserBelongsToTeams(userId string, teamIds []string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
props := make(map[string]interface{})
props["UserId"] = userId
idQuery := ""
for index, teamId := range teamIds {
if len(idQuery) > 0 {
idQuery += ", "
}
props["teamId"+strconv.Itoa(index)] = teamId
idQuery += ":teamId" + strconv.Itoa(index)
}
c, err := s.GetReplica().SelectInt("SELECT Count(*) FROM TeamMembers WHERE UserId = :UserId AND TeamId IN ("+idQuery+") AND DeleteAt = 0", props)
if err != nil {
result.Err = model.NewAppError("SqlTeamStore.UserBelongsToTeams", "store.sql_team.user_belongs_to_teams.app_error", nil, err.Error(), http.StatusInternalServerError)
return
}
result.Data = c > 0
})
}
func applyTeamMemberViewRestrictionsFilter(query sq.SelectBuilder, teamId string, restrictions *model.ViewUsersRestrictions) sq.SelectBuilder {
if restrictions == nil {
return query
}
// If you have no access to teams or channels, return and empty result.
if restrictions.Teams != nil && len(restrictions.Teams) == 0 && restrictions.Channels != nil && len(restrictions.Channels) == 0 {
return query.Where("1 = 0")
}
teams := make([]interface{}, len(restrictions.Teams))
for i, v := range restrictions.Teams {
teams[i] = v
}
channels := make([]interface{}, len(restrictions.Channels))
for i, v := range restrictions.Channels {
channels[i] = v
}
resultQuery := query.Join("Users ru ON (TeamMembers.UserId = ru.Id)")
if restrictions.Teams != nil && len(restrictions.Teams) > 0 {
resultQuery = resultQuery.Join(fmt.Sprintf("TeamMembers rtm ON ( rtm.UserId = ru.Id AND rtm.DeleteAt = 0 AND rtm.TeamId IN (%s))", sq.Placeholders(len(teams))), teams...)
}
if restrictions.Channels != nil && len(restrictions.Channels) > 0 {
resultQuery = resultQuery.Join(fmt.Sprintf("ChannelMembers rcm ON ( rcm.UserId = ru.Id AND rcm.ChannelId IN (%s))", sq.Placeholders(len(channels))), channels...)
}
return resultQuery.Distinct()
}

Просмотреть файл

@@ -407,6 +407,8 @@ func (us SqlUserStore) GetAllProfiles(options *model.UserGetOptions) store.Store
OrderBy("u.Username ASC").
Offset(uint64(options.Page * options.PerPage)).Limit(uint64(options.PerPage))
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
query = applyRoleFilter(query, options.Role, isPostgreSQL)
if options.Inactive {
@@ -466,6 +468,8 @@ func (us SqlUserStore) GetProfiles(options *model.UserGetOptions) store.StoreCha
OrderBy("u.Username ASC").
Offset(uint64(options.Page * options.PerPage)).Limit(uint64(options.PerPage))
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
query = applyRoleFilter(query, options.Role, isPostgreSQL)
if options.Inactive {
@@ -632,7 +636,7 @@ func (us SqlUserStore) GetAllProfilesInChannel(channelId string, allowFromCache
})
}
func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int) store.StoreChannel {
func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Join("TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )", teamId).
@@ -641,6 +645,8 @@ func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string,
OrderBy("u.Username ASC").
Offset(uint64(offset)).Limit(uint64(limit))
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInChannel", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -661,7 +667,7 @@ func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string,
})
}
func (us SqlUserStore) GetProfilesWithoutTeam(offset int, limit int) store.StoreChannel {
func (us SqlUserStore) GetProfilesWithoutTeam(offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Where(`(
@@ -676,6 +682,8 @@ func (us SqlUserStore) GetProfilesWithoutTeam(offset int, limit int) store.Store
OrderBy("u.Username ASC").
Offset(uint64(offset)).Limit(uint64(limit))
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfilesWithoutTeam", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -696,13 +704,11 @@ func (us SqlUserStore) GetProfilesWithoutTeam(offset int, limit int) store.Store
})
}
func (us SqlUserStore) GetProfilesByUsernames(usernames []string, teamId string) store.StoreChannel {
func (us SqlUserStore) GetProfilesByUsernames(usernames []string, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery
if teamId != "" {
query = query.Join("TeamMembers tm ON (tm.UserId = u.Id AND tm.TeamId = ?)", teamId)
}
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
query = query.
Where(map[string]interface{}{
@@ -731,7 +737,7 @@ type UserWithLastActivityAt struct {
LastActivityAt int64
}
func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string, offset, limit int) store.StoreChannel {
func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string, offset, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Column("s.LastActivityAt").
@@ -741,6 +747,8 @@ func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string, offset, limi
OrderBy("u.Username ASC").
Offset(uint64(offset)).Limit(uint64(limit))
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetRecentlyActiveUsers", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -766,7 +774,7 @@ func (us SqlUserStore) GetRecentlyActiveUsersForTeam(teamId string, offset, limi
})
}
func (us SqlUserStore) GetNewUsersForTeam(teamId string, offset, limit int) store.StoreChannel {
func (us SqlUserStore) GetNewUsersForTeam(teamId string, offset, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Join("TeamMembers tm ON (tm.UserId = u.Id AND tm.TeamId = ?)", teamId).
@@ -774,6 +782,8 @@ func (us SqlUserStore) GetNewUsersForTeam(teamId string, offset, limit int) stor
OrderBy("u.Username ASC").
Offset(uint64(offset)).Limit(uint64(limit))
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetNewUsersForTeam", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -794,7 +804,7 @@ func (us SqlUserStore) GetNewUsersForTeam(teamId string, offset, limit int) stor
})
}
func (us SqlUserStore) GetProfileByIds(userIds []string, allowFromCache bool) store.StoreChannel {
func (us SqlUserStore) GetProfileByIds(userIds []string, allowFromCache bool, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
users := []*model.User{}
remainingUserIds := make([]string, 0)
@@ -832,6 +842,8 @@ func (us SqlUserStore) GetProfileByIds(userIds []string, allowFromCache bool) st
}).
OrderBy("u.Username ASC")
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfileByIds", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -1039,18 +1051,18 @@ func (us SqlUserStore) PermanentDelete(userId string) store.StoreChannel {
func (us SqlUserStore) Count(options model.UserCountOptions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := sq.Select("COUNT(Users.Id)").From("Users")
query := sq.Select("COUNT(DISTINCT u.Id)").From("Users AS u")
if !options.IncludeDeleted {
query = query.Where("Users.DeleteAt = 0")
query = query.Where("u.DeleteAt = 0")
}
if options.IncludeBotAccounts {
if options.ExcludeRegularUsers {
query = query.Join("Bots ON Users.Id = Bots.UserId")
query = query.Join("Bots ON u.Id = Bots.UserId")
}
} else {
query = query.LeftJoin("Bots ON Users.Id = Bots.UserId").Where("Bots.UserId IS NULL")
query = query.LeftJoin("Bots ON u.Id = Bots.UserId").Where("Bots.UserId IS NULL")
if options.ExcludeRegularUsers {
// Currenty this doesn't make sense because it will always return 0
result.Err = model.NewAppError("SqlUserStore.Count", "store.sql_user.count.app_error", nil, "", http.StatusInternalServerError)
@@ -1059,8 +1071,9 @@ func (us SqlUserStore) Count(options model.UserCountOptions) store.StoreChannel
}
if options.TeamId != "" {
query = query.LeftJoin("TeamMembers ON Users.Id = TeamMembers.UserId").Where("TeamMembers.TeamId = ? AND TeamMembers.DeleteAt = 0", options.TeamId)
query = query.LeftJoin("TeamMembers AS tm ON u.Id = tm.UserId").Where("tm.TeamId = ? AND tm.DeleteAt = 0", options.TeamId)
}
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, false)
if us.DriverName() == model.DATABASE_DRIVER_POSTGRES {
query = query.PlaceholderFormat(sq.Dollar)
@@ -1285,6 +1298,8 @@ func (us SqlUserStore) performSearch(query sq.SelectBuilder, term string, option
query = generateSearchQuery(query, strings.Fields(term), searchType, isPostgreSQL)
}
query = applyViewRestrictionsFilter(query, options.ViewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.Search", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -1326,7 +1341,7 @@ func (us SqlUserStore) AnalyticsGetSystemAdminCount() store.StoreChannel {
})
}
func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int) store.StoreChannel {
func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
LeftJoin("TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )", teamId).
@@ -1334,6 +1349,8 @@ func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int
OrderBy("u.Username ASC").
Offset(uint64(offset)).Limit(uint64(limit))
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInTeam", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -1611,3 +1628,36 @@ func (us SqlUserStore) GetChannelGroupUsers(channelID string) store.StoreChannel
result.Data = users
})
}
func applyViewRestrictionsFilter(query sq.SelectBuilder, restrictions *model.ViewUsersRestrictions, distinct bool) sq.SelectBuilder {
if restrictions == nil {
return query
}
// If you have no access to teams or channels, return and empty result.
if restrictions.Teams != nil && len(restrictions.Teams) == 0 && restrictions.Channels != nil && len(restrictions.Channels) == 0 {
return query.Where("1 = 0")
}
teams := make([]interface{}, len(restrictions.Teams))
for i, v := range restrictions.Teams {
teams[i] = v
}
channels := make([]interface{}, len(restrictions.Channels))
for i, v := range restrictions.Channels {
channels[i] = v
}
resultQuery := query
if restrictions.Teams != nil && len(restrictions.Teams) > 0 {
resultQuery = resultQuery.Join(fmt.Sprintf("TeamMembers rtm ON ( rtm.UserId = u.Id AND rtm.DeleteAt = 0 AND rtm.TeamId IN (%s))", sq.Placeholders(len(teams))), teams...)
}
if restrictions.Channels != nil && len(restrictions.Channels) > 0 {
resultQuery = resultQuery.Join(fmt.Sprintf("ChannelMembers rcm ON ( rcm.UserId = u.Id AND rcm.ChannelId IN (%s))", sq.Placeholders(len(channels))), channels...)
}
if distinct {
return resultQuery.Distinct()
}
return resultQuery
}