* MM-34002: Improve AddUserToChannel When we would add a user to a channel, we would check whether the user is removed from that team or not. During LDAP sync, this check is not required because the team member would have just been created. Hence, we pass a boolean flag to bypass the check. And with that done, we can freely query the replica. https://mattermost.atlassian.net/browse/MM-34002 ```release-note NONE ``` * Refactor code * Rename a struct field * fix double negative
510 строки
16 KiB
Go
510 строки
16 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost-server/v5/model"
|
|
)
|
|
|
|
func TestCreateDefaultMemberships(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
singersTeam, err := th.App.CreateTeam(&model.Team{
|
|
DisplayName: "Singers",
|
|
Name: "zz" + model.NewId(),
|
|
Email: "singers@test.com",
|
|
Type: model.TEAM_OPEN,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("test team not created: %s", err.Error())
|
|
}
|
|
|
|
nerdsTeam, err := th.App.CreateTeam(&model.Team{
|
|
DisplayName: "Nerds",
|
|
Name: "zz" + model.NewId(),
|
|
Email: "nerds@test.com",
|
|
Type: model.TEAM_INVITE,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("test team not created: %s", err.Error())
|
|
}
|
|
|
|
practiceChannel, err := th.App.CreateChannel(&model.Channel{
|
|
TeamId: singersTeam.Id,
|
|
DisplayName: "Practices",
|
|
Name: model.NewId(),
|
|
Type: model.CHANNEL_OPEN,
|
|
}, false)
|
|
if err != nil {
|
|
t.Errorf("test channel not created: %s", err.Error())
|
|
}
|
|
|
|
experimentsChannel, err := th.App.CreateChannel(&model.Channel{
|
|
TeamId: singersTeam.Id,
|
|
DisplayName: "Experiments",
|
|
Name: model.NewId(),
|
|
Type: model.CHANNEL_PRIVATE,
|
|
}, false)
|
|
if err != nil {
|
|
t.Errorf("test channel not created: %s", err.Error())
|
|
}
|
|
|
|
gleeGroup, err := th.App.CreateGroup(&model.Group{
|
|
Name: model.NewString(model.NewId()),
|
|
DisplayName: "Glee Club",
|
|
RemoteId: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("test group not created: %s", err.Error())
|
|
}
|
|
|
|
scienceGroup, err := th.App.CreateGroup(&model.Group{
|
|
Name: model.NewString(model.NewId()),
|
|
DisplayName: "Science Club",
|
|
RemoteId: model.NewId(),
|
|
Source: model.GroupSourceLdap,
|
|
})
|
|
if err != nil {
|
|
t.Errorf("test group not created: %s", err.Error())
|
|
}
|
|
|
|
_, err = th.App.UpsertGroupSyncable(model.NewGroupChannel(gleeGroup.Id, practiceChannel.Id, true))
|
|
if err != nil {
|
|
t.Errorf("test groupchannel not created: %s", err.Error())
|
|
}
|
|
|
|
scienceTeamGroupSyncable, err := th.App.UpsertGroupSyncable(model.NewGroupTeam(scienceGroup.Id, nerdsTeam.Id, false))
|
|
if err != nil {
|
|
t.Errorf("test groupteam not created: %s", err.Error())
|
|
}
|
|
|
|
scienceChannelGroupSyncable, err := th.App.UpsertGroupSyncable(model.NewGroupChannel(scienceGroup.Id, experimentsChannel.Id, false))
|
|
if err != nil {
|
|
t.Errorf("test groupchannel not created: %s", err.Error())
|
|
}
|
|
|
|
singer1 := th.BasicUser
|
|
scientist1 := th.BasicUser2
|
|
|
|
_, err = th.App.UpsertGroupMember(gleeGroup.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("test groupmember not created: %s", err.Error())
|
|
}
|
|
|
|
scientistGroupMember, err := th.App.UpsertGroupMember(scienceGroup.Id, scientist1.Id)
|
|
if err != nil {
|
|
t.Errorf("test groupmember not created: %s", err.Error())
|
|
}
|
|
|
|
pErr := th.App.CreateDefaultMemberships(0)
|
|
if pErr != nil {
|
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
// Singer should be in team and channel
|
|
_, err = th.App.GetTeamMember(singersTeam.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team member: %s", err.Error())
|
|
}
|
|
_, err = th.App.GetChannelMember(context.Background(), practiceChannel.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving channel member: %s", err.Error())
|
|
}
|
|
|
|
tMembers, err := th.App.GetTeamMembers(singersTeam.Id, 0, 999, nil)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
expected := 1
|
|
actual := len(tMembers)
|
|
if actual != expected {
|
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
|
}
|
|
|
|
cMembersCount, err := th.App.GetChannelMemberCount(practiceChannel.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
if cMembersCount != int64(expected) {
|
|
t.Errorf("expected %d team member but got %d", expected, cMembersCount)
|
|
}
|
|
|
|
// Scientist should not be in team or channel
|
|
_, err = th.App.GetTeamMember(nerdsTeam.Id, scientist1.Id)
|
|
if err.Id != "app.team.get_member.missing.app_error" {
|
|
t.Errorf("wrong error: %s", err.Id)
|
|
}
|
|
|
|
_, err = th.App.GetChannelMember(context.Background(), experimentsChannel.Id, scientist1.Id)
|
|
if err.Id != "app.channel.get_member.missing.app_error" {
|
|
t.Errorf("wrong error: %s", err.Id)
|
|
}
|
|
|
|
tMembers, err = th.App.GetTeamMembers(nerdsTeam.Id, 0, 999, nil)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
expected = 0
|
|
actual = len(tMembers)
|
|
if actual != expected {
|
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
|
}
|
|
|
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
if cMembersCount != int64(expected) {
|
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
|
}
|
|
|
|
// update AutoAdd to true
|
|
scienceTeamGroupSyncable.AutoAdd = true
|
|
scienceTeamGroupSyncable, err = th.App.UpdateGroupSyncable(scienceTeamGroupSyncable)
|
|
if err != nil {
|
|
t.Errorf("error updating group syncable: %s", err.Error())
|
|
}
|
|
|
|
// Sync everything after syncable was created (proving that team updates trigger re-sync)
|
|
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
|
|
if pErr != nil {
|
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
// Scientist should be in team but not the channel
|
|
_, err = th.App.GetTeamMember(nerdsTeam.Id, scientist1.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team member: %s", err.Error())
|
|
}
|
|
|
|
_, err = th.App.GetChannelMember(context.Background(), experimentsChannel.Id, scientist1.Id)
|
|
if err.Id != "app.channel.get_member.missing.app_error" {
|
|
t.Errorf("wrong error: %s", err.Id)
|
|
}
|
|
|
|
tMembers, err = th.App.GetTeamMembers(nerdsTeam.Id, 0, 999, nil)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
expected = 1
|
|
actual = len(tMembers)
|
|
if actual != expected {
|
|
t.Errorf("expected %d team members but got %d", expected, actual)
|
|
}
|
|
|
|
expected = 0
|
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
if cMembersCount != int64(expected) {
|
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
|
}
|
|
|
|
// Update the channel syncable
|
|
scienceChannelGroupSyncable.AutoAdd = true
|
|
scienceChannelGroupSyncable, err = th.App.UpdateGroupSyncable(scienceChannelGroupSyncable)
|
|
if err != nil {
|
|
t.Errorf("error updating group syncable: %s", err.Error())
|
|
}
|
|
|
|
// Sync everything after syncable was created (proving that channel updates trigger re-sync)
|
|
pErr = th.App.CreateDefaultMemberships(scientistGroupMember.CreateAt + 1)
|
|
if pErr != nil {
|
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
expected = 1
|
|
cMembersCount, err = th.App.GetChannelMemberCount(experimentsChannel.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team members: %s", err.Error())
|
|
}
|
|
if cMembersCount != int64(expected) {
|
|
t.Errorf("expected %d team members but got %d", expected, cMembersCount)
|
|
}
|
|
|
|
// singer leaves team and channel
|
|
err = th.App.LeaveChannel(practiceChannel.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("error leaving channel: %s", err.Error())
|
|
}
|
|
err = th.App.LeaveTeam(singersTeam, singer1, "")
|
|
if err != nil {
|
|
t.Errorf("error leaving team: %s", err.Error())
|
|
}
|
|
|
|
// Even re-syncing from the beginning doesn't re-add to channel or team
|
|
pErr = th.App.CreateDefaultMemberships(0)
|
|
if pErr != nil {
|
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
// Singer should not be in team or channel
|
|
tMember, err := th.App.GetTeamMember(singersTeam.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("error retrieving team member: %s", err.Error())
|
|
}
|
|
if tMember.DeleteAt == 0 {
|
|
t.Error("expected team member to remain deleted")
|
|
}
|
|
|
|
_, err = th.App.GetChannelMember(context.Background(), practiceChannel.Id, singer1.Id)
|
|
if err == nil {
|
|
t.Error("Expected channel member to remain deleted")
|
|
}
|
|
|
|
// Ensure members are in channel
|
|
_, err = th.App.AddChannelMember(scientist1.Id, experimentsChannel, ChannelMemberOpts{})
|
|
if err != nil {
|
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
|
}
|
|
|
|
// Add other user so that user can leave channel
|
|
_, err = th.App.AddTeamMember(singersTeam.Id, singer1.Id)
|
|
if err != nil {
|
|
t.Errorf("unable to add user to team: %s", err.Error())
|
|
}
|
|
_, err = th.App.AddChannelMember(singer1.Id, experimentsChannel, ChannelMemberOpts{})
|
|
if err != nil {
|
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
|
}
|
|
|
|
// the channel syncable is updated
|
|
scienceChannelGroupSyncable, err = th.App.UpdateGroupSyncable(scienceChannelGroupSyncable)
|
|
if err != nil {
|
|
t.Errorf("error updating group syncable: %s", err.Error())
|
|
}
|
|
|
|
pErr = th.App.CreateDefaultMemberships(0)
|
|
if pErr != nil {
|
|
t.Errorf("faild to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
timeBeforeLeaving := model.GetMillis()
|
|
|
|
// User leaves channel
|
|
err = th.App.LeaveChannel(experimentsChannel.Id, scientist1.Id)
|
|
if err != nil {
|
|
t.Errorf("unable to add user to channel: %s", err.Error())
|
|
}
|
|
|
|
timeAfterLeaving := model.GetMillis()
|
|
|
|
// Purging channelmemberhistory doesn't re-add user to channel
|
|
_, nErr := th.App.Srv().Store.ChannelMemberHistory().PermanentDeleteBatch(timeBeforeLeaving, 1000)
|
|
if nErr != nil {
|
|
t.Errorf("error permanently deleting channelmemberhistory: %s", nErr.Error())
|
|
}
|
|
|
|
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
|
|
if pErr != nil {
|
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
_, err = th.App.GetChannelMember(context.Background(), experimentsChannel.Id, scientist1.Id)
|
|
if err == nil {
|
|
t.Error("Expected channel member to remain deleted")
|
|
}
|
|
|
|
// Purging channelmemberhistory doesn't re-add user to channel
|
|
_, nErr = th.App.Srv().Jobs.Store.ChannelMemberHistory().PermanentDeleteBatch(timeAfterLeaving, 1000)
|
|
if nErr != nil {
|
|
t.Errorf("error permanently deleting channelmemberhistory: %s", nErr.Error())
|
|
}
|
|
|
|
pErr = th.App.CreateDefaultMemberships(scienceChannelGroupSyncable.UpdateAt)
|
|
if pErr != nil {
|
|
t.Errorf("failed to populate syncables: %s", pErr.Error())
|
|
}
|
|
|
|
// Channel member is re-added.
|
|
_, err = th.App.GetChannelMember(context.Background(), experimentsChannel.Id, scientist1.Id)
|
|
if err != nil {
|
|
t.Errorf("expected channel member: %s", err.Error())
|
|
}
|
|
|
|
t.Run("Team with restricted domains skips over members that do not match the allowed domains", func(t *testing.T) {
|
|
restrictedUser := th.CreateUser()
|
|
restrictedUser.Email = "restricted@mattermost.org"
|
|
_, err = th.App.UpdateUser(restrictedUser, false)
|
|
require.Nil(t, err)
|
|
_, err = th.App.UpsertGroupMember(scienceGroup.Id, restrictedUser.Id)
|
|
require.Nil(t, err)
|
|
|
|
restrictedTeam, err := th.App.CreateTeam(&model.Team{
|
|
DisplayName: "Restricted",
|
|
Name: "restricted" + model.NewId(),
|
|
Email: "restricted@mattermost.org",
|
|
AllowedDomains: "mattermost.org",
|
|
Type: model.TEAM_OPEN,
|
|
})
|
|
require.Nil(t, err)
|
|
_, err = th.App.UpsertGroupSyncable(model.NewGroupTeam(scienceGroup.Id, restrictedTeam.Id, true))
|
|
require.Nil(t, err)
|
|
|
|
restrictedChannel, err := th.App.CreateChannel(&model.Channel{
|
|
TeamId: restrictedTeam.Id,
|
|
DisplayName: "Restricted",
|
|
Name: "restricted" + model.NewId(),
|
|
Type: model.CHANNEL_OPEN,
|
|
}, false)
|
|
require.Nil(t, err)
|
|
_, err = th.App.UpsertGroupSyncable(model.NewGroupChannel(scienceGroup.Id, restrictedChannel.Id, true))
|
|
require.Nil(t, err)
|
|
|
|
pErr = th.App.CreateDefaultMemberships(0)
|
|
require.NoError(t, pErr)
|
|
|
|
// Ensure only the restricted user was added to both the team and channel
|
|
cMembersCount, err = th.App.GetChannelMemberCount(restrictedChannel.Id)
|
|
require.Nil(t, err)
|
|
require.Equal(t, cMembersCount, int64(1))
|
|
tmembers, err := th.App.GetTeamMembers(restrictedTeam.Id, 0, 100, nil)
|
|
require.Nil(t, err)
|
|
require.Len(t, tmembers, 1)
|
|
require.Equal(t, tmembers[0].UserId, restrictedUser.Id)
|
|
})
|
|
}
|
|
|
|
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, ChannelMemberOpts{})
|
|
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.UpsertGroupSyncable(model.NewGroupTeam(group.Id, team.Id, true))
|
|
require.Nil(t, err)
|
|
_, err = th.App.UpsertGroupSyncable(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, nil)
|
|
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.UpsertGroupMember(group.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, err)
|
|
|
|
// run the delete
|
|
appErr := th.App.DeleteGroupConstrainedMemberships()
|
|
require.NoError(t, appErr)
|
|
|
|
// verify the new member counts
|
|
tmembers, err = th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, nil)
|
|
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)
|
|
}
|
|
|
|
func TestSyncSyncableRoles(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
team := th.CreateTeam()
|
|
|
|
channel := th.CreateChannel(team)
|
|
channel.GroupConstrained = model.NewBool(true)
|
|
channel, err := th.App.UpdateChannel(channel)
|
|
require.Nil(t, err)
|
|
|
|
user1 := th.CreateUser()
|
|
user2 := th.CreateUser()
|
|
group := th.CreateGroup()
|
|
|
|
teamSyncable, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group.Id,
|
|
})
|
|
require.Nil(t, err)
|
|
|
|
channelSyncable, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{
|
|
SyncableId: channel.Id,
|
|
Type: model.GroupSyncableTypeChannel,
|
|
GroupId: group.Id,
|
|
})
|
|
require.Nil(t, err)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
_, err = th.App.UpsertGroupMember(group.Id, user.Id)
|
|
require.Nil(t, err)
|
|
|
|
var tm *model.TeamMember
|
|
tm, err = th.App.AddTeamMember(team.Id, user.Id)
|
|
require.Nil(t, err)
|
|
require.False(t, tm.SchemeAdmin)
|
|
|
|
cm := th.AddUserToChannel(user, channel)
|
|
require.False(t, cm.SchemeAdmin)
|
|
}
|
|
|
|
teamSyncable.SchemeAdmin = true
|
|
_, err = th.App.UpdateGroupSyncable(teamSyncable)
|
|
require.Nil(t, err)
|
|
|
|
channelSyncable.SchemeAdmin = true
|
|
_, err = th.App.UpdateGroupSyncable(channelSyncable)
|
|
require.Nil(t, err)
|
|
|
|
err = th.App.SyncSyncableRoles(channel.Id, model.GroupSyncableTypeChannel)
|
|
require.Nil(t, err)
|
|
|
|
err = th.App.SyncSyncableRoles(team.Id, model.GroupSyncableTypeTeam)
|
|
require.Nil(t, err)
|
|
|
|
for _, user := range []*model.User{user1, user2} {
|
|
tm, err := th.App.GetTeamMember(team.Id, user.Id)
|
|
require.Nil(t, err)
|
|
require.True(t, tm.SchemeAdmin)
|
|
|
|
cm, err := th.App.GetChannelMember(context.Background(), channel.Id, user.Id)
|
|
require.Nil(t, err)
|
|
require.True(t, cm.SchemeAdmin)
|
|
}
|
|
}
|