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
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
5b70962f71
Коммит
c8920588a0
@@ -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
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user