MM-14412: Enables force removals of team/channel members base on group constraints. (#10490)

* MM-14412: Enables force removals of team/channel members base on group constraints.

* Renames some existing methods and variables.

* Change return types to ChannelMembers and TeamMembers for some existing methods.

* Adds option to change LDAP_DATA to either 'qa' or 'test' with env variable.

* Adds methods to retrieve ChannelMembers and TeamMembers that, based on group constraints, should be deleted.

* Adds helper functions to create GroupTeams and GroupChannels.

* MM-14412: Switches to helper methods for GroupSyncable creation in test files.

* MM-14412: Style fix.

* MM-14412: Switches remaining GroupSyncable instances to be created with helper functions.

* MM-14412: Typo fix.

* MM-11412: Build fixes.

* MM-14412: Checks if user is team member before re-adding.

* MM-14412: Update for change of GroupConstrained fields type.
Этот коммит содержится в:
Martin Kraft
2019-04-02 09:22:50 -04:00
коммит произвёл GitHub
родитель cee65379c7
Коммит 25fd962016
23 изменённых файлов: 1212 добавлений и 607 удалений

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

@@ -133,18 +133,34 @@ func (a *App) DeleteGroupSyncable(groupID string, syncableID string, syncableTyp
return result.Data.(*model.GroupSyncable), nil
}
func (a *App) PendingAutoAddTeamMembers(minGroupMembersCreateAt int64) ([]*model.UserTeamIDPair, *model.AppError) {
result := <-a.Srv.Store.Group().PendingAutoAddTeamMembers(minGroupMembersCreateAt)
func (a *App) TeamMembersToAdd(since int64) ([]*model.UserTeamIDPair, *model.AppError) {
result := <-a.Srv.Store.Group().TeamMembersToAdd(since)
if result.Err != nil {
return nil, result.Err
}
return result.Data.([]*model.UserTeamIDPair), nil
}
func (a *App) PendingAutoAddChannelMembers(minGroupMembersCreateAt int64) ([]*model.UserChannelIDPair, *model.AppError) {
result := <-a.Srv.Store.Group().PendingAutoAddChannelMembers(minGroupMembersCreateAt)
func (a *App) ChannelMembersToAdd(since int64) ([]*model.UserChannelIDPair, *model.AppError) {
result := <-a.Srv.Store.Group().ChannelMembersToAdd(since)
if result.Err != nil {
return nil, result.Err
}
return result.Data.([]*model.UserChannelIDPair), nil
}
func (a *App) TeamMembersToRemove() ([]*model.TeamMember, *model.AppError) {
result := <-a.Srv.Store.Group().TeamMembersToRemove()
if result.Err != nil {
return nil, result.Err
}
return result.Data.([]*model.TeamMember), nil
}
func (a *App) ChannelMembersToRemove() ([]*model.ChannelMember, *model.AppError) {
result := <-a.Srv.Store.Group().ChannelMembersToRemove()
if result.Err != nil {
return nil, result.Err
}
return result.Data.([]*model.ChannelMember), nil
}

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

@@ -135,12 +135,7 @@ func TestCreateGroupSyncable(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
group := th.CreateGroup()
groupSyncable := &model.GroupSyncable{
GroupId: group.Id,
AutoAdd: false,
SyncableId: th.BasicTeam.Id,
Type: model.GroupSyncableTypeTeam,
}
groupSyncable := model.NewGroupTeam(group.Id, th.BasicTeam.Id, false)
gs, err := th.App.CreateGroupSyncable(groupSyncable)
require.Nil(t, err)
@@ -155,12 +150,7 @@ func TestGetGroupSyncable(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
group := th.CreateGroup()
groupSyncable := &model.GroupSyncable{
GroupId: group.Id,
AutoAdd: false,
SyncableId: th.BasicTeam.Id,
Type: model.GroupSyncableTypeTeam,
}
groupSyncable := model.NewGroupTeam(group.Id, th.BasicTeam.Id, false)
gs, err := th.App.CreateGroupSyncable(groupSyncable)
require.Nil(t, err)
@@ -177,12 +167,7 @@ func TestGetGroupSyncables(t *testing.T) {
group := th.CreateGroup()
// Create a group team
groupSyncable := &model.GroupSyncable{
GroupId: group.Id,
AutoAdd: false,
SyncableId: th.BasicTeam.Id,
Type: model.GroupSyncableTypeTeam,
}
groupSyncable := model.NewGroupTeam(group.Id, th.BasicTeam.Id, false)
gs, err := th.App.CreateGroupSyncable(groupSyncable)
require.Nil(t, err)
@@ -198,12 +183,7 @@ func TestDeleteGroupSyncable(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
group := th.CreateGroup()
groupChannel := &model.GroupSyncable{
GroupId: group.Id,
AutoAdd: false,
SyncableId: th.BasicChannel.Id,
Type: model.GroupSyncableTypeChannel,
}
groupChannel := model.NewGroupChannel(group.Id, th.BasicChannel.Id, false)
gs, err := th.App.CreateGroupSyncable(groupChannel)
require.Nil(t, err)

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

@@ -7,15 +7,15 @@ import (
"github.com/mattermost/mattermost-server/mlog"
)
// PopulateSyncablesSince adds users to teams and channels based on their group memberships and how those groups are
// CreateDefaultMemberships adds users to teams and channels based on their group memberships and how those groups are
// configured to sync with teams and channels for group members on or after the given timestamp.
func (a *App) PopulateSyncablesSince(groupMembersCreatedAfter int64) error {
userTeamIDs, appErr := a.PendingAutoAddTeamMembers(groupMembersCreatedAfter)
func (a *App) CreateDefaultMemberships(since int64) error {
teamMembers, appErr := a.TeamMembersToAdd(since)
if appErr != nil {
return appErr
}
for _, userTeam := range userTeamIDs {
for _, userTeam := range teamMembers {
_, err := a.AddTeamMember(userTeam.TeamID, userTeam.UserID)
if err != nil {
return err
@@ -27,26 +27,33 @@ func (a *App) PopulateSyncablesSince(groupMembersCreatedAfter int64) error {
)
}
userChannelIDs, appErr := a.PendingAutoAddChannelMembers(groupMembersCreatedAfter)
channelMembers, appErr := a.ChannelMembersToAdd(since)
if appErr != nil {
return appErr
}
for _, userChannel := range userChannelIDs {
for _, userChannel := range channelMembers {
channel, err := a.GetChannel(userChannel.ChannelID)
if err != nil {
return err
}
// First add user to team
_, err = a.AddTeamMember(channel.TeamId, userChannel.UserID)
if err != nil {
tmem, err := a.GetTeamMember(channel.TeamId, userChannel.UserID)
if err != nil && err.Id != "store.sql_team.get_member.missing.app_error" {
return err
}
a.Log.Info("added teammember",
mlog.String("user_id", userChannel.UserID),
mlog.String("team_id", channel.TeamId),
)
// First add user to team
if tmem == nil {
_, err = a.AddTeamMember(channel.TeamId, userChannel.UserID)
if err != nil {
return err
}
a.Log.Info("added teammember",
mlog.String("user_id", userChannel.UserID),
mlog.String("team_id", channel.TeamId),
)
}
_, err = a.AddChannelMember(userChannel.UserID, channel, "", "", "")
if err != nil {
@@ -61,3 +68,48 @@ func (a *App) PopulateSyncablesSince(groupMembersCreatedAfter int64) error {
return nil
}
// DeleteGroupConstrainedMemberships deletes team and channel memberships of users who aren't members of the allowed
// groups of all group-constrained teams and channels.
func (a *App) DeleteGroupConstrainedMemberships() error {
channelMembers, appErr := a.ChannelMembersToRemove()
if appErr != nil {
return appErr
}
for _, userChannel := range channelMembers {
channel, err := a.GetChannel(userChannel.ChannelId)
if err != nil {
return err
}
err = a.RemoveUserFromChannel(userChannel.UserId, "", channel)
if err != nil {
return err
}
a.Log.Info("removed channelmember",
mlog.String("user_id", userChannel.UserId),
mlog.String("channel_id", channel.Id),
)
}
teamMembers, appErr := a.TeamMembersToRemove()
if appErr != nil {
return appErr
}
for _, userTeam := range teamMembers {
err := a.RemoveUserFromTeam(userTeam.TeamId, userTeam.UserId, "")
if err != nil {
return err
}
a.Log.Info("removed teammember",
mlog.String("user_id", userTeam.UserId),
mlog.String("team_id", userTeam.TeamId),
)
}
return nil
}

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

@@ -4,9 +4,10 @@ import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/stretchr/testify/require"
)
func TestPopulateSyncablesSince(t *testing.T) {
func TestCreateDefaultMemberships(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
@@ -70,32 +71,17 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("test group not created: %s", err.Error())
}
_, err = th.App.CreateGroupSyncable(&model.GroupSyncable{
AutoAdd: true,
GroupId: gleeGroup.Id,
SyncableId: practiceChannel.Id,
Type: model.GroupSyncableTypeChannel,
})
_, err = th.App.CreateGroupSyncable(model.NewGroupChannel(gleeGroup.Id, practiceChannel.Id, true))
if err != nil {
t.Errorf("test groupchannel not created: %s", err.Error())
}
scienceTeamGroupSyncable, err := th.App.CreateGroupSyncable(&model.GroupSyncable{
AutoAdd: false,
GroupId: scienceGroup.Id,
SyncableId: nerdsTeam.Id,
Type: model.GroupSyncableTypeTeam,
})
scienceTeamGroupSyncable, err := th.App.CreateGroupSyncable(model.NewGroupTeam(scienceGroup.Id, nerdsTeam.Id, false))
if err != nil {
t.Errorf("test groupteam not created: %s", err.Error())
}
scienceChannelGroupSyncable, err := th.App.CreateGroupSyncable(&model.GroupSyncable{
AutoAdd: false,
GroupId: scienceGroup.Id,
SyncableId: experimentsChannel.Id,
Type: model.GroupSyncableTypeChannel,
})
scienceChannelGroupSyncable, err := th.App.CreateGroupSyncable(model.NewGroupChannel(scienceGroup.Id, experimentsChannel.Id, false))
if err != nil {
t.Errorf("test groupchannel not created: %s", err.Error())
}
@@ -113,7 +99,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("test groupmember not created: %s", err.Error())
}
pErr := th.App.PopulateSyncablesSince(0)
pErr := th.App.CreateDefaultMemberships(0)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -183,7 +169,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
}
// Sync everything after syncable was created (proving that team updates trigger re-sync)
pErr = th.App.PopulateSyncablesSince(scientistGroupMember.CreateAt + 1)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -226,7 +212,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
}
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
pErr = th.App.PopulateSyncablesSince(scientistGroupMember.CreateAt + 1)
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -251,7 +237,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
}
// Even re-syncing from the beginning doesn't re-add to channel or team
pErr = th.App.PopulateSyncablesSince(0)
pErr = th.App.CreateDefaultMemberships(0)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -292,7 +278,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("error updating group syncable: %s", err.Error())
}
pErr = th.App.PopulateSyncablesSince(0)
pErr = th.App.CreateDefaultMemberships(0)
if pErr != nil {
t.Errorf("faild to populate syncables: %s", pErr.Error())
}
@@ -313,7 +299,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("error permanently deleting channelmemberhistory: %s", result.Err.Error())
}
pErr = th.App.PopulateSyncablesSince(scienceChannelGroupSyncable.UpdateAt)
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
if pErr != nil {
t.Errorf("failed to populate syncables: %s", pErr.Error())
}
@@ -329,7 +315,7 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("error permanently deleting channelmemberhistory: %s", result.Err.Error())
}
pErr = th.App.PopulateSyncablesSince(scienceChannelGroupSyncable.UpdateAt)
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
if pErr != nil {
t.Errorf("failed to populate syncables: %s", pErr.Error())
}
@@ -340,3 +326,70 @@ func TestPopulateSyncablesSince(t *testing.T) {
t.Errorf("expected channel member: %s", err.Error())
}
}
func TestDeleteGroupMemberships(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
group := th.CreateGroup()
userIDs := []string{th.BasicUser.Id, th.BasicUser2.Id, th.SystemAdminUser.Id}
var err *model.AppError
// add users to teams and channels
for _, userID := range userIDs {
_, err = th.App.AddTeamMember(th.BasicTeam.Id, userID)
require.Nil(t, err)
_, err = th.App.AddChannelMember(userID, th.BasicChannel, "", "", "")
require.Nil(t, err)
}
// make team group-constrained
team := th.BasicTeam
team.GroupConstrained = model.NewBool(true)
team, err = th.App.UpdateTeam(team)
require.Nil(t, err)
require.True(t, *team.GroupConstrained)
// make channel group-constrained
channel := th.BasicChannel
channel.GroupConstrained = model.NewBool(true)
channel, err = th.App.UpdateChannel(channel)
require.Nil(t, err)
require.True(t, *channel.GroupConstrained)
// create groupteam and groupchannel
_, err = th.App.CreateGroupSyncable(model.NewGroupTeam(group.Id, team.Id, true))
require.Nil(t, err)
_, err = th.App.CreateGroupSyncable(model.NewGroupChannel(group.Id, channel.Id, true))
require.Nil(t, err)
// verify the member count
tmembers, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100)
require.Nil(t, err)
require.Len(t, tmembers, 3)
cmemberCount, err := th.App.GetChannelMemberCount(th.BasicChannel.Id)
require.Nil(t, err)
require.Equal(t, 3, int(cmemberCount))
// add a user to the group
_, err = th.App.CreateOrRestoreGroupMember(group.Id, th.SystemAdminUser.Id)
require.Nil(t, err)
// run the delete
appErr := th.App.DeleteGroupConstrainedMemberships()
require.Nil(t, appErr)
// verify the new member counts
tmembers, err = th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100)
require.Nil(t, err)
require.Len(t, tmembers, 1)
require.Equal(t, th.SystemAdminUser.Id, tmembers[0].UserId)
cmembers, err := th.App.GetChannelMembersPage(channel.Id, 0, 99)
require.Nil(t, err)
require.Len(t, (*cmembers), 1)
require.Equal(t, th.SystemAdminUser.Id, (*cmembers)[0].UserId)
}

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

@@ -122,6 +122,7 @@ func (a *App) UpdateTeam(team *model.Team) (*model.Team, *model.AppError) {
oldTeam.CompanyName = team.CompanyName
oldTeam.AllowedDomains = team.AllowedDomains
oldTeam.LastTeamIconUpdate = team.LastTeamIconUpdate
oldTeam.GroupConstrained = team.GroupConstrained
oldTeam, err = a.updateTeamUnsanitized(oldTeam)
if err != nil {