[MM-14751] Adds group_constrained filter to user list and search endpoints (#10678)

Этот коммит содержится в:
Miguel de la Cruz
2019-05-16 10:12:06 +01:00
коммит произвёл GitHub
родитель beb7592c93
Коммит 098dbc84cc
9 изменённых файлов: 206 добавлений и 85 удалений

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

@@ -448,6 +448,54 @@ func applyRoleFilter(query sq.SelectBuilder, role string, isPostgreSQL bool) sq.
return query.Where("u.Roles LIKE ? ESCAPE '*'", roleParam)
}
func applyChannelGroupConstrainedFilter(query sq.SelectBuilder, channelId string) sq.SelectBuilder {
if channelId == "" {
return query
}
return query.
Where(`u.Id IN (
SELECT
GroupMembers.UserId
FROM
Channels
JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id
JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
WHERE
Channels.Id = ?
AND GroupChannels.DeleteAt = 0
AND UserGroups.DeleteAt = 0
AND GroupMembers.DeleteAt = 0
GROUP BY
GroupMembers.UserId
)`, channelId)
}
func applyTeamGroupConstrainedFilter(query sq.SelectBuilder, teamId string) sq.SelectBuilder {
if teamId == "" {
return query
}
return query.
Where(`u.Id IN (
SELECT
GroupMembers.UserId
FROM
Teams
JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id
JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
WHERE
Teams.Id = ?
AND GroupTeams.DeleteAt = 0
AND UserGroups.DeleteAt = 0
AND GroupMembers.DeleteAt = 0
GROUP BY
GroupMembers.UserId
)`, teamId)
}
func (s SqlUserStore) GetEtagForProfiles(teamId string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
updateAt, err := s.GetReplica().SelectInt("SELECT UpdateAt FROM Users, TeamMembers WHERE TeamMembers.TeamId = :TeamId AND Users.Id = TeamMembers.UserId ORDER BY UpdateAt DESC LIMIT 1", map[string]interface{}{"TeamId": teamId})
@@ -636,7 +684,7 @@ func (us SqlUserStore) GetAllProfilesInChannel(channelId string, allowFromCache
})
}
func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string, groupConstrained bool, 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).
@@ -647,6 +695,10 @@ func (us SqlUserStore) GetProfilesNotInChannel(teamId string, channelId string,
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
if groupConstrained {
query = applyChannelGroupConstrainedFilter(query, channelId)
}
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInChannel", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -1185,6 +1237,10 @@ func (us SqlUserStore) SearchNotInTeam(notInTeamId string, term string, options
OrderBy("u.Username ASC").
Limit(uint64(options.Limit))
if options.GroupConstrained {
query = applyTeamGroupConstrainedFilter(query, notInTeamId)
}
*result = us.performSearch(query, term, options)
})
}
@@ -1201,6 +1257,10 @@ func (us SqlUserStore) SearchNotInChannel(teamId string, channelId string, term
query = query.Join("TeamMembers tm ON ( tm.UserId = u.Id AND tm.DeleteAt = 0 AND tm.TeamId = ? )", teamId)
}
if options.GroupConstrained {
query = applyChannelGroupConstrainedFilter(query, channelId)
}
*result = us.performSearch(query, term, options)
})
}
@@ -1341,7 +1401,7 @@ func (us SqlUserStore) AnalyticsGetSystemAdminCount() store.StoreChannel {
})
}
func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
func (us SqlUserStore) GetProfilesNotInTeam(teamId string, groupConstrained bool, 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).
@@ -1351,6 +1411,10 @@ func (us SqlUserStore) GetProfilesNotInTeam(teamId string, offset int, limit int
query = applyViewRestrictionsFilter(query, viewRestrictions, true)
if groupConstrained {
query = applyTeamGroupConstrainedFilter(query, teamId)
}
queryString, args, err := query.ToSql()
if err != nil {
result.Err = model.NewAppError("SqlUserStore.GetProfilesNotInTeam", "store.sql_user.app_error", nil, err.Error(), http.StatusInternalServerError)
@@ -1551,23 +1615,7 @@ func (us SqlUserStore) GetUsersBatchForIndexing(startTime, endTime int64, limit
func (us SqlUserStore) GetTeamGroupUsers(teamID string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Where(`Id IN (
SELECT
GroupMembers.UserId
FROM
Teams
JOIN GroupTeams ON GroupTeams.TeamId = Teams.Id
JOIN UserGroups ON UserGroups.Id = GroupTeams.GroupId
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
WHERE
Teams.Id = ?
AND GroupTeams.DeleteAt = 0
AND UserGroups.DeleteAt = 0
AND GroupMembers.DeleteAt = 0
GROUP BY
GroupMembers.UserId
)`, teamID)
query := applyTeamGroupConstrainedFilter(us.usersQuery, teamID)
queryString, args, err := query.ToSql()
if err != nil {
@@ -1591,23 +1639,7 @@ func (us SqlUserStore) GetTeamGroupUsers(teamID string) store.StoreChannel {
func (us SqlUserStore) GetChannelGroupUsers(channelID string) store.StoreChannel {
return store.Do(func(result *store.StoreResult) {
query := us.usersQuery.
Where(`Id IN (
SELECT
GroupMembers.UserId
FROM
Channels
JOIN GroupChannels ON GroupChannels.ChannelId = Channels.Id
JOIN UserGroups ON UserGroups.Id = GroupChannels.GroupId
JOIN GroupMembers ON GroupMembers.GroupId = UserGroups.Id
WHERE
Channels.Id = ?
AND GroupChannels.DeleteAt = 0
AND UserGroups.DeleteAt = 0
AND GroupMembers.DeleteAt = 0
GROUP BY
GroupMembers.UserId
)`, channelID)
query := applyChannelGroupConstrainedFilter(us.usersQuery, channelID)
queryString, args, err := query.ToSql()
if err != nil {

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

@@ -261,7 +261,7 @@ type UserStore interface {
GetProfilesInChannel(channelId string, offset int, limit int) StoreChannel
GetProfilesInChannelByStatus(channelId string, offset int, limit int) StoreChannel
GetAllProfilesInChannel(channelId string, allowFromCache bool) StoreChannel
GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetProfilesNotInChannel(teamId string, channelId string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetProfilesWithoutTeam(offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetProfilesByUsernames(usernames []string, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetAllProfiles(options *model.UserGetOptions) StoreChannel
@@ -292,7 +292,7 @@ type UserStore interface {
SearchWithoutTeam(term string, options *model.UserSearchOptions) StoreChannel
AnalyticsGetInactiveUsersCount() StoreChannel
AnalyticsGetSystemAdminCount() StoreChannel
GetProfilesNotInTeam(teamId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetProfilesNotInTeam(teamId string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) StoreChannel
GetEtagForProfilesNotInTeam(teamId string) StoreChannel
ClearAllCustomRoleAssignments() StoreChannel
InferSystemInstallDate() StoreChannel

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

@@ -443,13 +443,13 @@ func (_m *UserStore) GetProfilesInChannelByStatus(channelId string, offset int,
return r0
}
// GetProfilesNotInChannel provides a mock function with given fields: teamId, channelId, offset, limit, viewRestrictions
func (_m *UserStore) GetProfilesNotInChannel(teamId string, channelId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
ret := _m.Called(teamId, channelId, offset, limit, viewRestrictions)
// GetProfilesNotInChannel provides a mock function with given fields: teamId, channelId, groupConstrained, offset, limit, viewRestrictions
func (_m *UserStore) GetProfilesNotInChannel(teamId string, channelId string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
ret := _m.Called(teamId, channelId, groupConstrained, offset, limit, viewRestrictions)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, string, int, int, *model.ViewUsersRestrictions) store.StoreChannel); ok {
r0 = rf(teamId, channelId, offset, limit, viewRestrictions)
if rf, ok := ret.Get(0).(func(string, string, bool, int, int, *model.ViewUsersRestrictions) store.StoreChannel); ok {
r0 = rf(teamId, channelId, groupConstrained, offset, limit, viewRestrictions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)
@@ -459,13 +459,13 @@ func (_m *UserStore) GetProfilesNotInChannel(teamId string, channelId string, of
return r0
}
// GetProfilesNotInTeam provides a mock function with given fields: teamId, offset, limit, viewRestrictions
func (_m *UserStore) GetProfilesNotInTeam(teamId string, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
ret := _m.Called(teamId, offset, limit, viewRestrictions)
// GetProfilesNotInTeam provides a mock function with given fields: teamId, groupConstrained, offset, limit, viewRestrictions
func (_m *UserStore) GetProfilesNotInTeam(teamId string, groupConstrained bool, offset int, limit int, viewRestrictions *model.ViewUsersRestrictions) store.StoreChannel {
ret := _m.Called(teamId, groupConstrained, offset, limit, viewRestrictions)
var r0 store.StoreChannel
if rf, ok := ret.Get(0).(func(string, int, int, *model.ViewUsersRestrictions) store.StoreChannel); ok {
r0 = rf(teamId, offset, limit, viewRestrictions)
if rf, ok := ret.Get(0).(func(string, bool, int, int, *model.ViewUsersRestrictions) store.StoreChannel); ok {
r0 = rf(teamId, groupConstrained, offset, limit, viewRestrictions)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.StoreChannel)

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

@@ -1031,7 +1031,7 @@ func testUserStoreGetProfilesNotInChannel(t *testing.T, ss store.Store) {
}, -1)).(*model.Channel)
t.Run("get team 1, channel 1, offset 0, limit 100", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, 0, 100, nil)
result := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, false, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u1),
@@ -1041,7 +1041,7 @@ func testUserStoreGetProfilesNotInChannel(t *testing.T, ss store.Store) {
})
t.Run("get team 1, channel 2, offset 0, limit 100", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, 0, 100, nil)
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, false, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u1),
@@ -1075,19 +1075,55 @@ func testUserStoreGetProfilesNotInChannel(t *testing.T, ss store.Store) {
}))
t.Run("get team 1, channel 1, offset 0, limit 100, after update", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, 0, 100, nil)
result := <-ss.User().GetProfilesNotInChannel(teamId, c1.Id, false, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{}, result.Data.([]*model.User))
})
t.Run("get team 1, channel 2, offset 0, limit 100, after update", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, 0, 100, nil)
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, false, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u2),
sanitized(u3),
}, result.Data.([]*model.User))
})
t.Run("get team 1, channel 2, offset 0, limit 0, setting group constrained when it's not", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, true, 0, 100, nil)
require.Nil(t, result.Err)
assert.Empty(t, result.Data.([]*model.User))
})
// create a group
group := store.Must(ss.Group().Create(&model.Group{
Name: "n_" + model.NewId(),
DisplayName: "dn_" + model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: "ri_" + model.NewId(),
})).(*model.Group)
// add two members to the group
for _, u := range []*model.User{u1, u2} {
res := <-ss.Group().CreateOrRestoreMember(group.Id, u.Id)
require.Nil(t, res.Err)
}
// associate the group with the channel
res := <-ss.Group().CreateGroupSyncable(&model.GroupSyncable{
GroupId: group.Id,
SyncableId: c2.Id,
Type: model.GroupSyncableTypeChannel,
})
require.Nil(t, res.Err)
t.Run("get team 1, channel 2, offset 0, limit 0, setting group constrained", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInChannel(teamId, c2.Id, true, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u2),
}, result.Data.([]*model.User))
})
}
func testUserStoreGetProfilesByIds(t *testing.T, ss store.Store) {
@@ -3069,7 +3105,14 @@ func testUserStoreAnalyticsGetSystemAdminCount(t *testing.T, ss store.Store) {
}
func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
teamId := model.NewId()
team, err := ss.Team().Save(&model.Team{
DisplayName: "Team",
Name: model.NewId(),
Type: model.TEAM_OPEN,
})
require.Nil(t, err)
teamId := team.Id
teamId2 := model.NewId()
u1 := store.Must(ss.User().Save(&model.User{
@@ -3114,7 +3157,7 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
})
t.Run("get not in team 1, offset 0, limit 100000", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000, nil)
result := <-ss.User().GetProfilesNotInTeam(teamId, false, 0, 100000, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u2),
@@ -3123,7 +3166,7 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
})
t.Run("get not in team 1, offset 1, limit 1", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, 1, 1, nil)
result := <-ss.User().GetProfilesNotInTeam(teamId, false, 1, 1, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u3),
@@ -3131,7 +3174,7 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
})
t.Run("get not in team 2, offset 0, limit 100", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId2, 0, 100, nil)
result := <-ss.User().GetProfilesNotInTeam(teamId2, false, 0, 100, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u1),
@@ -3154,7 +3197,7 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
})
t.Run("get not in team 1, offset 0, limit 100000 after update", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000, nil)
result := <-ss.User().GetProfilesNotInTeam(teamId, false, 0, 100000, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u3),
@@ -3178,7 +3221,7 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
})
t.Run("get not in team 1, offset 0, limit 100000 after second update", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, 0, 100000, nil)
result := <-ss.User().GetProfilesNotInTeam(teamId, false, 0, 100000, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u1),
@@ -3220,6 +3263,43 @@ func testUserStoreGetProfilesNotInTeam(t *testing.T, ss store.Store) {
etag4 := result.Data.(string)
require.Equal(t, etag3, etag4, "etag should not have changed")
})
t.Run("get not in team 1, offset 0, limit 100000 after second update, setting group constrained when it's not", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, true, 0, 100000, nil)
require.Nil(t, result.Err)
assert.Empty(t, result.Data.([]*model.User))
})
// create a group
group := store.Must(ss.Group().Create(&model.Group{
Name: "n_" + model.NewId(),
DisplayName: "dn_" + model.NewId(),
Source: model.GroupSourceLdap,
RemoteId: "ri_" + model.NewId(),
})).(*model.Group)
// add two members to the group
for _, u := range []*model.User{u1, u2} {
res := <-ss.Group().CreateOrRestoreMember(group.Id, u.Id)
require.Nil(t, res.Err)
}
// associate the group with the team
res := <-ss.Group().CreateGroupSyncable(&model.GroupSyncable{
GroupId: group.Id,
SyncableId: teamId,
Type: model.GroupSyncableTypeTeam,
})
require.Nil(t, res.Err)
t.Run("get not in team 1, offset 0, limit 100000 after second update, setting group constrained", func(t *testing.T) {
result := <-ss.User().GetProfilesNotInTeam(teamId, true, 0, 100000, nil)
require.Nil(t, result.Err)
assert.Equal(t, []*model.User{
sanitized(u1),
sanitized(u2),
}, result.Data.([]*model.User))
})
}
func testUserStoreClearAllCustomRoleAssignments(t *testing.T, ss store.Store) {