Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1797 lines
62 KiB
Go
1797 lines
62 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/app/email"
|
|
emailmocks "github.com/mattermost/mattermost/server/v8/channels/app/email/mocks"
|
|
"github.com/mattermost/mattermost/server/v8/channels/app/teams"
|
|
"github.com/mattermost/mattermost/server/v8/channels/app/users"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store/sqlstore"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks"
|
|
"github.com/mattermost/mattermost/server/v8/channels/testlib"
|
|
)
|
|
|
|
func TestCreateTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
id := model.NewId()
|
|
team := &model.Team{
|
|
DisplayName: "dn_" + id,
|
|
Name: "name" + id,
|
|
Email: "success+" + id + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
|
|
_, err := th.App.CreateTeam(th.Context, team)
|
|
require.Nil(t, err, "Should create a new team")
|
|
|
|
_, err = th.App.CreateTeam(th.Context, th.BasicTeam)
|
|
require.NotNil(t, err, "Should not create a new team - team already exist")
|
|
}
|
|
|
|
func TestCreateTeamWithUser(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
id := model.NewId()
|
|
team := &model.Team{
|
|
DisplayName: "dn_" + id,
|
|
Name: "name" + id,
|
|
Email: "success+" + id + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
|
|
_, err := th.App.CreateTeamWithUser(th.Context, team, th.BasicUser.Id)
|
|
require.Nil(t, err, "Should create a new team with existing user")
|
|
|
|
_, err = th.App.CreateTeamWithUser(th.Context, team, model.NewId())
|
|
require.NotNil(t, err, "Should not create a new team - user does not exist")
|
|
}
|
|
|
|
func TestUpdateTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.BasicTeam.DisplayName = "Testing 123"
|
|
|
|
updatedTeam, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
require.Equal(t, "Testing 123", updatedTeam.DisplayName, "Wrong Team DisplayName")
|
|
}
|
|
|
|
func TestAddUserToTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
t.Run("add user", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err, "Should add user to the team")
|
|
})
|
|
|
|
t.Run("allow user by domain", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "example.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err, "Should have allowed whitelisted user")
|
|
})
|
|
|
|
t.Run("block user by domain but allow bot", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "example.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "test@invalid.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, err := th.App.CreateUser(th.Context, &user)
|
|
require.Nil(t, err, "Error creating user: %s", err)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.NotNil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.NotNil(t, err, "Should not add restricted user")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
|
|
user = model.User{Email: strings.ToLower(model.NewId()) + "test@invalid.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), AuthService: "notnil", AuthData: model.NewPointer("notnil")}
|
|
ruser, err = th.App.CreateUser(th.Context, &user)
|
|
require.Nil(t, err, "Error creating authservice user: %s", err)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.NotNil(t, err, "Should not add authservice user")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
|
|
bot, err := th.App.CreateBot(th.Context, &model.Bot{
|
|
Username: "somebot",
|
|
Description: "a bot",
|
|
OwnerId: th.BasicUser.Id,
|
|
})
|
|
require.Nil(t, err)
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, bot.UserId, "")
|
|
assert.Nil(t, err, "should be able to add bot to domain restricted team")
|
|
})
|
|
|
|
t.Run("block user with subdomain", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "example.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "test@invalid.example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.NotNil(t, err, "Should not add restricted user")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
})
|
|
|
|
t.Run("allow users by multiple domains", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "foo.com, bar.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@foo.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser1, _ := th.App.CreateUser(th.Context, &user1)
|
|
|
|
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@bar.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser2, _ := th.App.CreateUser(th.Context, &user2)
|
|
|
|
user3 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@invalid.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser3, _ := th.App.CreateUser(th.Context, &user3)
|
|
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user1)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user2)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user3)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser1.Id, "")
|
|
require.Nil(t, err, "Should have allowed whitelisted user1")
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser2.Id, "")
|
|
require.Nil(t, err, "Should have allowed whitelisted user2")
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser3.Id, "")
|
|
require.NotNil(t, err, "Should not have allowed restricted user3")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
})
|
|
|
|
t.Run("should set up initial sidebar categories when joining a team", func(t *testing.T) {
|
|
user := th.CreateUser()
|
|
team := th.CreateTeam()
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, team.Id, user.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
res, err := th.App.GetSidebarCategoriesForTeamForUser(th.Context, user.Id, team.Id)
|
|
require.Nil(t, err)
|
|
assert.Len(t, res.Categories, 3)
|
|
assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type)
|
|
assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type)
|
|
assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type)
|
|
})
|
|
}
|
|
|
|
func TestAddUserToTeamByToken(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
rguest := th.CreateGuest()
|
|
|
|
t.Run("invalid token", func(t *testing.T) {
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, "123")
|
|
require.NotNil(t, err, "Should fail on unexisting token")
|
|
})
|
|
|
|
t.Run("invalid token type", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeVerifyEmail,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
defer func() {
|
|
appErr := th.App.DeleteToken(token)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.NotNil(t, err, "Should fail on bad token type")
|
|
})
|
|
|
|
t.Run("expired token", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
|
|
token.CreateAt = model.GetMillis() - InvitationExpiryTime - 1
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
defer func() {
|
|
appErr := th.App.DeleteToken(token)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.NotNil(t, err, "Should fail on expired token")
|
|
})
|
|
|
|
t.Run("invalid team id", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": model.NewId()}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
defer func() {
|
|
appErr := th.App.DeleteToken(token)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.NotNil(t, err, "Should fail on bad team id")
|
|
})
|
|
|
|
t.Run("invalid user id", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
defer func() {
|
|
appErr := th.App.DeleteToken(token)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, model.NewId(), token.Token)
|
|
require.NotNil(t, err, "Should fail on bad user id")
|
|
})
|
|
|
|
t.Run("valid request", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.Nil(t, err, "Should add user to the team")
|
|
|
|
_, nErr := th.App.Srv().Store().Token().GetByToken(token.Token)
|
|
require.Error(t, nErr, "The token must be deleted after be used")
|
|
|
|
members, err := th.App.GetChannelMembersForUser(th.Context, th.BasicTeam.Id, ruser.Id)
|
|
require.Nil(t, err)
|
|
assert.Len(t, members, 2)
|
|
})
|
|
|
|
t.Run("invalid add a guest using a regular invite", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, rguest.Id, token.Token)
|
|
assert.NotNil(t, err)
|
|
})
|
|
|
|
t.Run("invalid add a regular user using a guest invite", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeGuestInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
assert.NotNil(t, err)
|
|
})
|
|
|
|
t.Run("invalid add a guest user with a non-granted email domain", func(t *testing.T) {
|
|
restrictedDomain := *th.App.Config().GuestAccountsSettings.RestrictCreationToDomains
|
|
defer func() {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GuestAccountsSettings.RestrictCreationToDomains = &restrictedDomain })
|
|
}()
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.RestrictCreationToDomains = "restricted.com" })
|
|
token := model.NewToken(
|
|
TokenTypeGuestInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, rguest.Id, token.Token)
|
|
require.NotNil(t, err)
|
|
assert.Equal(t, "api.team.join_user_to_team.allowed_domains.app_error", err.Id)
|
|
})
|
|
|
|
t.Run("add a guest user with a granted email domain", func(t *testing.T) {
|
|
restrictedDomain := *th.App.Config().GuestAccountsSettings.RestrictCreationToDomains
|
|
defer func() {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.GuestAccountsSettings.RestrictCreationToDomains = &restrictedDomain })
|
|
}()
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.RestrictCreationToDomains = "restricted.com" })
|
|
token := model.NewToken(
|
|
TokenTypeGuestInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
|
)
|
|
guestEmail := rguest.Email
|
|
rguest.Email = "test@restricted.com"
|
|
_, err := th.App.Srv().Store().User().Update(th.Context, rguest, false)
|
|
th.App.InvalidateCacheForUser(rguest.Id)
|
|
require.NoError(t, err)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, appErr := th.App.AddUserToTeamByToken(th.Context, rguest.Id, token.Token)
|
|
require.Nil(t, appErr)
|
|
rguest.Email = guestEmail
|
|
_, err = th.App.Srv().Store().User().Update(th.Context, rguest, false)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("add a guest user even though there are team and system domain restrictions", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "restricted-team.com"
|
|
_, err := th.Server.Store().Team().Update(th.BasicTeam)
|
|
require.NoError(t, err)
|
|
restrictedDomain := *th.App.Config().TeamSettings.RestrictCreationToDomains
|
|
defer func() {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.RestrictCreationToDomains = &restrictedDomain })
|
|
}()
|
|
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictCreationToDomains = "restricted.com" })
|
|
token := model.NewToken(
|
|
TokenTypeGuestInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
|
)
|
|
_, err = th.App.Srv().Store().User().Update(th.Context, rguest, false)
|
|
require.NoError(t, err)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
_, _, appErr := th.App.AddUserToTeamByToken(th.Context, rguest.Id, token.Token)
|
|
require.Nil(t, appErr)
|
|
th.BasicTeam.AllowedDomains = ""
|
|
_, err = th.Server.Store().Team().Update(th.BasicTeam)
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("valid request from guest invite", func(t *testing.T) {
|
|
token := model.NewToken(
|
|
TokenTypeGuestInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "channels": th.BasicChannel.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, rguest.Id, token.Token)
|
|
require.Nil(t, err, "Should add user to the team")
|
|
|
|
_, nErr := th.App.Srv().Store().Token().GetByToken(token.Token)
|
|
require.Error(t, nErr, "The token must be deleted after be used")
|
|
|
|
members, err := th.App.GetChannelMembersForUser(th.Context, th.BasicTeam.Id, rguest.Id)
|
|
require.Nil(t, err)
|
|
require.Len(t, members, 1)
|
|
assert.Equal(t, members[0].ChannelId, th.BasicChannel.Id)
|
|
})
|
|
|
|
t.Run("group-constrained team", func(t *testing.T) {
|
|
th.BasicTeam.GroupConstrained = model.NewPointer(true)
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
|
|
_, _, err = th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.NotNil(t, err, "Should return an error when trying to join a group-constrained team.")
|
|
require.Equal(t, "app.team.invite_token.group_constrained.error", err.Id)
|
|
|
|
th.BasicTeam.GroupConstrained = model.NewPointer(false)
|
|
_, err = th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
})
|
|
|
|
t.Run("block user", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "example.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "test@invalid.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
|
|
_, _, err = th.App.AddUserToTeamByToken(th.Context, ruser.Id, token.Token)
|
|
require.NotNil(t, err, "Should not add restricted user")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
})
|
|
|
|
t.Run("should set up initial sidebar categories when joining a team by token", func(t *testing.T) {
|
|
user := th.CreateUser()
|
|
team := th.CreateTeam()
|
|
|
|
token := model.NewToken(
|
|
TokenTypeTeamInvitation,
|
|
model.MapToJSON(map[string]string{"teamId": team.Id}),
|
|
)
|
|
require.NoError(t, th.App.Srv().Store().Token().Save(token))
|
|
|
|
_, _, err := th.App.AddUserToTeamByToken(th.Context, user.Id, token.Token)
|
|
require.Nil(t, err)
|
|
|
|
res, err := th.App.GetSidebarCategoriesForTeamForUser(th.Context, user.Id, team.Id)
|
|
require.Nil(t, err)
|
|
assert.Len(t, res.Categories, 3)
|
|
assert.Equal(t, model.SidebarCategoryFavorites, res.Categories[0].Type)
|
|
assert.Equal(t, model.SidebarCategoryChannels, res.Categories[1].Type)
|
|
assert.Equal(t, model.SidebarCategoryDirectMessages, res.Categories[2].Type)
|
|
})
|
|
}
|
|
|
|
func TestAddUserToTeamByTeamId(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
t.Run("add user", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
|
|
err := th.App.AddUserToTeamByTeamId(th.Context, th.BasicTeam.Id, ruser)
|
|
require.Nil(t, err, "Should add user to the team")
|
|
})
|
|
|
|
t.Run("block user", func(t *testing.T) {
|
|
th.BasicTeam.AllowedDomains = "example.com"
|
|
_, err := th.App.UpdateTeam(th.BasicTeam)
|
|
require.Nil(t, err, "Should update the team")
|
|
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "test@invalid.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
err = th.App.AddUserToTeamByTeamId(th.Context, th.BasicTeam.Id, ruser)
|
|
require.NotNil(t, err, "Should not add restricted user")
|
|
require.Equal(t, "JoinUserToTeam", err.Where, "Error should be JoinUserToTeam")
|
|
})
|
|
}
|
|
|
|
func TestAdjustTeamsFromProductLimits(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
teams := []*model.Team{
|
|
{
|
|
DisplayName: "team-1",
|
|
Name: "team-1",
|
|
Email: "foo@foo.com",
|
|
Type: model.TeamOpen,
|
|
},
|
|
{
|
|
DisplayName: "team-2",
|
|
Name: "team-2",
|
|
Email: "foo@foo.com",
|
|
Type: model.TeamOpen,
|
|
},
|
|
{
|
|
DisplayName: "team-3",
|
|
Name: "team-3",
|
|
Email: "foo@foo.com",
|
|
Type: model.TeamOpen,
|
|
},
|
|
}
|
|
teamIds := []string{}
|
|
for _, create := range teams {
|
|
team, err := th.App.CreateTeam(th.Context, create)
|
|
require.Nil(t, err)
|
|
teamIds = append(teamIds, team.Id)
|
|
}
|
|
t.Run("Should soft delete teams if there are more teams than the limit", func(t *testing.T) {
|
|
activeLimit := 1
|
|
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
|
|
|
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
|
|
teamsList, err := th.App.GetTeams(teamIds)
|
|
|
|
require.Nil(t, err)
|
|
|
|
// Sort the list of teams based on their creation date
|
|
sort.Slice(teamsList, func(i, j int) bool {
|
|
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
|
})
|
|
|
|
for i := range teamsList {
|
|
require.Equal(t, teamsList[i].DisplayName, teams[i].DisplayName)
|
|
require.NotEqual(t, 0, teamsList[i].DeleteAt)
|
|
require.Equal(t, true, teamsList[i].CloudLimitsArchived)
|
|
}
|
|
})
|
|
|
|
t.Run("Should not do anything if the amount of teams is equal to the limit", func(t *testing.T) {
|
|
expectedTeamsList, err := th.App.GetAllTeams()
|
|
|
|
var expectedActiveTeams []*model.Team
|
|
var expectedCloudArchivedTeams []*model.Team
|
|
for _, team := range expectedTeamsList {
|
|
if team.DeleteAt == 0 {
|
|
expectedActiveTeams = append(expectedActiveTeams, team)
|
|
}
|
|
if team.DeleteAt > 0 && team.CloudLimitsArchived {
|
|
expectedCloudArchivedTeams = append(expectedCloudArchivedTeams, team)
|
|
}
|
|
}
|
|
|
|
require.Nil(t, err)
|
|
|
|
activeLimit := len(expectedActiveTeams)
|
|
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
|
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
|
|
actualTeamsList, err := th.App.GetAllTeams()
|
|
|
|
require.Nil(t, err)
|
|
var actualActiveTeams []*model.Team
|
|
var actualCloudArchivedTeams []*model.Team
|
|
for _, team := range actualTeamsList {
|
|
if team.DeleteAt == 0 {
|
|
actualActiveTeams = append(actualActiveTeams, team)
|
|
}
|
|
if team.DeleteAt > 0 && team.CloudLimitsArchived {
|
|
actualCloudArchivedTeams = append(actualCloudArchivedTeams, team)
|
|
}
|
|
}
|
|
|
|
require.Equal(t, len(expectedActiveTeams), len(actualActiveTeams))
|
|
require.Equal(t, len(expectedCloudArchivedTeams), len(actualCloudArchivedTeams))
|
|
})
|
|
|
|
t.Run("Should restore archived teams if limit increases", func(t *testing.T) {
|
|
activeLimit := 1
|
|
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
|
|
|
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
activeLimit = 10000 // make the limit extremely high so all teams are enabled
|
|
teamLimits = &model.TeamsLimits{Active: &activeLimit}
|
|
|
|
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
|
|
teamsList, err := th.App.GetTeams(teamIds)
|
|
|
|
require.Nil(t, err)
|
|
|
|
// Sort the list of teams based on their creation date
|
|
sort.Slice(teamsList, func(i, j int) bool {
|
|
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
|
})
|
|
|
|
for i := range teamsList {
|
|
require.Equal(t, teamsList[i].DisplayName, teams[i].DisplayName)
|
|
require.Equal(t, int64(0), teamsList[i].DeleteAt)
|
|
require.Equal(t, false, teamsList[i].CloudLimitsArchived)
|
|
}
|
|
})
|
|
|
|
t.Run("Should only restore teams that were archived by cloud limits", func(t *testing.T) {
|
|
activeLimit := 1
|
|
teamLimits := &model.TeamsLimits{Active: &activeLimit}
|
|
|
|
err := th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
|
|
cloudLimitsArchived := false
|
|
patch := &model.TeamPatch{CloudLimitsArchived: &cloudLimitsArchived}
|
|
team, err := th.App.PatchTeam(teamIds[0], patch)
|
|
require.Nil(t, err)
|
|
require.Equal(t, false, team.CloudLimitsArchived)
|
|
|
|
activeLimit = 10000 // make the limit extremely high so all teams are enabled
|
|
teamLimits = &model.TeamsLimits{Active: &activeLimit}
|
|
|
|
err = th.App.AdjustTeamsFromProductLimits(teamLimits)
|
|
require.Nil(t, err)
|
|
|
|
teamsList, err := th.App.GetTeams(teamIds)
|
|
|
|
require.Nil(t, err)
|
|
|
|
// Sort the list of teams based on their creation date
|
|
sort.Slice(teamsList, func(i, j int) bool {
|
|
return teamsList[i].CreateAt < teamsList[j].CreateAt
|
|
})
|
|
|
|
require.NotEqual(t, int64(0), teamsList[0].DeleteAt)
|
|
require.Equal(t, int64(0), teamsList[1].DeleteAt)
|
|
require.Equal(t, int64(0), teamsList[2].DeleteAt)
|
|
})
|
|
}
|
|
|
|
func TestPermanentDeleteTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
team, err := th.App.CreateTeam(th.Context, &model.Team{
|
|
DisplayName: "deletion-test",
|
|
Name: "deletion-test",
|
|
Email: "foo@foo.com",
|
|
Type: model.TeamOpen,
|
|
})
|
|
require.Nil(t, err, "Should create a team")
|
|
|
|
command, err := th.App.CreateCommand(&model.Command{
|
|
CreatorId: th.BasicUser.Id,
|
|
TeamId: team.Id,
|
|
Trigger: "foo",
|
|
URL: "http://foo",
|
|
Method: model.CommandMethodPost,
|
|
})
|
|
require.Nil(t, err, "Should create a command")
|
|
|
|
command, err = th.App.GetCommand(command.Id)
|
|
require.NotNil(t, command, "command should not be nil")
|
|
require.Nil(t, err, "unable to get new command")
|
|
|
|
err = th.App.PermanentDeleteTeam(th.Context, team)
|
|
require.Nil(t, err)
|
|
|
|
command, appErr := th.App.GetCommand(command.Id)
|
|
require.Nil(t, command, "command was deleted")
|
|
require.NotNil(t, appErr, "unable to get command")
|
|
|
|
// Test deleting a team with no channels.
|
|
team = th.CreateTeam()
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteTeam(th.Context, team)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
channels, err := th.App.GetPublicChannelsForTeam(th.Context, team.Id, 0, 1000)
|
|
require.Nil(t, err)
|
|
|
|
for _, channel := range channels {
|
|
err2 := th.App.PermanentDeleteChannel(th.Context, channel)
|
|
require.Nil(t, err2)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
team := &model.Team{
|
|
Id: model.NewId(),
|
|
Email: th.MakeEmail(),
|
|
InviteId: model.NewId(),
|
|
AllowedDomains: "example.com",
|
|
}
|
|
|
|
copyTeam := func() *model.Team {
|
|
copy := &model.Team{} //nolint:revive
|
|
*copy = *team
|
|
return copy
|
|
}
|
|
|
|
t.Run("not a user of the team", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: model.NewId(),
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.Empty(t, sanitized.Email, "should've sanitized team")
|
|
require.Empty(t, sanitized.InviteId, "should've sanitized inviteid")
|
|
})
|
|
|
|
t.Run("user of the team", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: team.Id,
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.Empty(t, sanitized.Email, "should've sanitized team")
|
|
require.NotEmpty(t, sanitized.InviteId, "should have not sanitized inviteid")
|
|
})
|
|
|
|
t.Run("team admin", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: team.Id,
|
|
Roles: model.TeamUserRoleId + " " + model.TeamAdminRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.NotEmpty(t, sanitized.Email, "shouldn't have sanitized team")
|
|
require.NotEmpty(t, sanitized.InviteId, "shouldn't have sanitized inviteid")
|
|
})
|
|
|
|
t.Run("team admin of another team", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: model.NewId(),
|
|
Roles: model.TeamUserRoleId + " " + model.TeamAdminRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.Empty(t, sanitized.Email, "should've sanitized team")
|
|
require.Empty(t, sanitized.InviteId, "should've sanitized inviteid")
|
|
})
|
|
|
|
t.Run("system admin, not a user of team", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId + " " + model.SystemAdminRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: model.NewId(),
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.NotEmpty(t, sanitized.Email, "shouldn't have sanitized team")
|
|
require.NotEmpty(t, sanitized.InviteId, "shouldn't have sanitized inviteid")
|
|
})
|
|
|
|
t.Run("system admin, user of team", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId + " " + model.SystemAdminRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: team.Id,
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeam(session, copyTeam())
|
|
require.NotEmpty(t, sanitized.Email, "shouldn't have sanitized team")
|
|
require.NotEmpty(t, sanitized.InviteId, "shouldn't have sanitized inviteid")
|
|
})
|
|
}
|
|
|
|
func TestSanitizeTeams(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
t.Run("not a system admin", func(t *testing.T) {
|
|
teams := []*model.Team{
|
|
{
|
|
Id: model.NewId(),
|
|
Email: th.MakeEmail(),
|
|
AllowedDomains: "example.com",
|
|
},
|
|
{
|
|
Id: model.NewId(),
|
|
Email: th.MakeEmail(),
|
|
AllowedDomains: "example.com",
|
|
},
|
|
}
|
|
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: teams[0].Id,
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
{
|
|
UserId: userID,
|
|
TeamId: teams[1].Id,
|
|
Roles: model.TeamUserRoleId + " " + model.TeamAdminRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeams(session, teams)
|
|
|
|
require.Empty(t, sanitized[0].Email, "should've sanitized first team")
|
|
require.NotEmpty(t, sanitized[1].Email, "shouldn't have sanitized second team")
|
|
})
|
|
|
|
t.Run("system admin", func(t *testing.T) {
|
|
teams := []*model.Team{
|
|
{
|
|
Id: model.NewId(),
|
|
Email: th.MakeEmail(),
|
|
AllowedDomains: "example.com",
|
|
},
|
|
{
|
|
Id: model.NewId(),
|
|
Email: th.MakeEmail(),
|
|
AllowedDomains: "example.com",
|
|
},
|
|
}
|
|
|
|
userID := model.NewId()
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId + " " + model.SystemAdminRoleId,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: userID,
|
|
TeamId: teams[0].Id,
|
|
Roles: model.TeamUserRoleId,
|
|
},
|
|
},
|
|
}
|
|
|
|
sanitized := th.App.SanitizeTeams(session, teams)
|
|
assert.NotEmpty(t, sanitized[0].Email, "shouldn't have sanitized first team")
|
|
assert.NotEmpty(t, sanitized[1].Email, "shouldn't have sanitized second team")
|
|
})
|
|
}
|
|
|
|
func TestJoinUserToTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
id := model.NewId()
|
|
team := &model.Team{
|
|
DisplayName: "dn_" + id,
|
|
Name: "name" + id,
|
|
Email: "success+" + id + "@simulator.amazonses.com",
|
|
Type: model.TeamOpen,
|
|
}
|
|
|
|
_, err := th.App.CreateTeam(th.Context, team)
|
|
require.Nil(t, err, "Should create a new team")
|
|
|
|
maxUsersPerTeam := th.App.Config().TeamSettings.MaxUsersPerTeam
|
|
defer func() {
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.MaxUsersPerTeam = maxUsersPerTeam })
|
|
appErr := th.App.PermanentDeleteTeam(th.Context, team)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
one := 1
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.MaxUsersPerTeam = &one })
|
|
|
|
t.Run("new join", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, appErr := th.App.JoinUserToTeam(th.Context, team, ruser, "")
|
|
require.Nil(t, appErr, "Should return no error")
|
|
})
|
|
|
|
t.Run("new join with limit problem", func(t *testing.T) {
|
|
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser1, _ := th.App.CreateUser(th.Context, &user1)
|
|
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser2, _ := th.App.CreateUser(th.Context, &user2)
|
|
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user1)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user2)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, appErr := th.App.JoinUserToTeam(th.Context, team, ruser1, ruser2.Id)
|
|
require.Nil(t, appErr, "Should return no error")
|
|
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, team, ruser2, ruser1.Id)
|
|
require.NotNil(t, appErr, "Should fail")
|
|
})
|
|
|
|
t.Run("re-join after leaving with limit problem", func(t *testing.T) {
|
|
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser1, _ := th.App.CreateUser(th.Context, &user1)
|
|
|
|
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser2, _ := th.App.CreateUser(th.Context, &user2)
|
|
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user1)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user2)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, appErr := th.App.JoinUserToTeam(th.Context, team, ruser1, ruser2.Id)
|
|
require.Nil(t, appErr, "Should return no error")
|
|
appErr = th.App.LeaveTeam(th.Context, team, ruser1, ruser1.Id)
|
|
require.Nil(t, appErr, "Should return no error")
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, team, ruser2, ruser2.Id)
|
|
require.Nil(t, appErr, "Should return no error")
|
|
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, team, ruser1, ruser2.Id)
|
|
require.NotNil(t, appErr, "Should fail")
|
|
})
|
|
|
|
t.Run("new join with correct scheme_admin value from group syncable", func(t *testing.T) {
|
|
user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser1, _ := th.App.CreateUser(th.Context, &user1)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user1)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
group := th.CreateGroup()
|
|
|
|
_, err = th.App.UpsertGroupMember(group.Id, user1.Id)
|
|
require.Nil(t, err)
|
|
|
|
gs, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{
|
|
AutoAdd: true,
|
|
SyncableId: team.Id,
|
|
Type: model.GroupSyncableTypeTeam,
|
|
GroupId: group.Id,
|
|
SchemeAdmin: false,
|
|
})
|
|
require.Nil(t, err)
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.MaxUsersPerTeam = model.NewPointer(999) })
|
|
|
|
tm1, appErr := th.App.JoinUserToTeam(th.Context, team, ruser1, "")
|
|
require.Nil(t, appErr)
|
|
require.False(t, tm1.SchemeAdmin)
|
|
|
|
user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser2, _ := th.App.CreateUser(th.Context, &user2)
|
|
defer func() {
|
|
appErr = th.App.PermanentDeleteUser(th.Context, &user2)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, err = th.App.UpsertGroupMember(group.Id, user2.Id)
|
|
require.Nil(t, err)
|
|
|
|
gs.SchemeAdmin = true
|
|
_, err = th.App.UpdateGroupSyncable(gs)
|
|
require.Nil(t, err)
|
|
|
|
tm2, appErr := th.App.JoinUserToTeam(th.Context, team, ruser2, "")
|
|
require.Nil(t, appErr)
|
|
require.True(t, tm2.SchemeAdmin)
|
|
})
|
|
}
|
|
|
|
func TestLeaveTeamPanic(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := SetupWithStoreMock(t)
|
|
defer th.TearDown()
|
|
|
|
mockStore := th.App.Srv().Store().(*mocks.Store)
|
|
mockUserStore := mocks.UserStore{}
|
|
mockUserStore.On("Get", context.Background(), "userID").Return(&model.User{Id: "userID"}, nil)
|
|
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
|
|
|
|
mockChannelStore := mocks.ChannelStore{}
|
|
mockChannelStore.On("Get", "channelID", true).Return(&model.Channel{Id: "channelID"}, nil)
|
|
mockChannelStore.On("GetMember", context.Background(), "channelID", "userID").Return(&model.ChannelMember{
|
|
NotifyProps: model.StringMap{
|
|
model.PushNotifyProp: model.ChannelNotifyDefault,
|
|
},
|
|
}, nil)
|
|
mockChannelStore.On("GetChannels", "myteam", "userID", mock.Anything).Return(model.ChannelList{}, nil)
|
|
|
|
var err error
|
|
th.App.ch.srv.userService, err = users.New(users.ServiceConfig{
|
|
UserStore: &mockUserStore,
|
|
SessionStore: &mocks.SessionStore{},
|
|
OAuthStore: &mocks.OAuthStore{},
|
|
ConfigFn: th.App.ch.srv.platform.Config,
|
|
LicenseFn: th.App.ch.srv.License,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
mockPreferenceStore := mocks.PreferenceStore{}
|
|
mockPreferenceStore.On("Get", "userID", model.PreferenceCategoryDisplaySettings, model.PreferenceNameCollapsedThreadsEnabled).Return(&model.Preference{Value: "on"}, nil)
|
|
|
|
mockPostStore := mocks.PostStore{}
|
|
mockPostStore.On("GetMaxPostSize").Return(65535, nil)
|
|
|
|
mockSystemStore := mocks.SystemStore{}
|
|
mockSystemStore.On("GetByName", "UpgradedFromTE").Return(&model.System{Name: "UpgradedFromTE", Value: "false"}, nil)
|
|
mockSystemStore.On("GetByName", "InstallationDate").Return(&model.System{Name: "InstallationDate", Value: "10"}, nil)
|
|
mockSystemStore.On("GetByName", "FirstServerRunTimestamp").Return(&model.System{Name: "FirstServerRunTimestamp", Value: "10"}, nil)
|
|
mockLicenseStore := mocks.LicenseStore{}
|
|
mockLicenseStore.On("Get", "").Return(&model.LicenseRecord{}, nil)
|
|
|
|
mockTeamStore := mocks.TeamStore{}
|
|
mockTeamStore.On("GetMember", mock.AnythingOfType("*request.Context"), "myteam", "userID").Return(&model.TeamMember{TeamId: "myteam", UserId: "userID"}, nil).Run(func(args mock.Arguments) {
|
|
c, ok := args[0].(request.CTX)
|
|
require.True(t, ok)
|
|
|
|
sqlstore.HasMaster(c.Context())
|
|
})
|
|
mockTeamStore.On("UpdateMember", mock.Anything, mock.Anything).Return(nil, errors.New("repro error")) // This is the line that triggers the error
|
|
|
|
mockStore.On("Channel").Return(&mockChannelStore)
|
|
mockStore.On("Preference").Return(&mockPreferenceStore)
|
|
mockStore.On("Post").Return(&mockPostStore)
|
|
mockStore.On("User").Return(&mockUserStore)
|
|
mockStore.On("System").Return(&mockSystemStore)
|
|
mockStore.On("License").Return(&mockLicenseStore)
|
|
mockStore.On("Team").Return(&mockTeamStore)
|
|
mockStore.On("GetDBSchemaVersion").Return(1, nil)
|
|
|
|
team := &model.Team{Id: "myteam"}
|
|
user := &model.User{Id: "userID"}
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.ExperimentalEnableDefaultChannelLeaveJoinMessages = false
|
|
})
|
|
|
|
th.App.ch.srv.teamService, err = teams.New(teams.ServiceConfig{
|
|
TeamStore: &mockTeamStore,
|
|
ChannelStore: &mockChannelStore,
|
|
GroupStore: &mocks.GroupStore{},
|
|
Users: th.App.ch.srv.userService,
|
|
WebHub: th.App.ch.srv.platform,
|
|
ConfigFn: th.App.ch.srv.platform.Config,
|
|
LicenseFn: th.App.ch.srv.License,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.NotPanics(t, func() {
|
|
appErr := th.App.LeaveTeam(th.Context, team, user, user.Id)
|
|
require.NotNil(t, appErr)
|
|
}, "unexpected panic from LeaveTeam")
|
|
}
|
|
|
|
func TestAppUpdateTeamScheme(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
team := th.BasicTeam
|
|
mockID := model.NewPointer("x")
|
|
team.SchemeId = mockID
|
|
|
|
updatedTeam, appErr := th.App.UpdateTeamScheme(th.BasicTeam)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, mockID, updatedTeam.SchemeId, "Wrong Team SchemeId")
|
|
|
|
// Test that a newly applied team scheme applies the new permissions to a team member
|
|
err := th.App.SetPhase2PermissionsMigrationStatus(true)
|
|
require.NoError(t, err)
|
|
|
|
team2Scheme := th.SetupTeamScheme()
|
|
channelUser, appErr := th.App.GetRoleByName(th.Context, team2Scheme.DefaultChannelUserRole)
|
|
require.Nil(t, appErr)
|
|
channelUser.Permissions = []string{}
|
|
_, appErr = th.App.UpdateRole(channelUser) // Remove all permissions from the team user role of the scheme
|
|
require.Nil(t, appErr)
|
|
|
|
channelAdmin, appErr := th.App.GetRoleByName(th.Context, team2Scheme.DefaultChannelAdminRole)
|
|
require.Nil(t, appErr)
|
|
channelAdmin.Permissions = []string{}
|
|
_, appErr = th.App.UpdateRole(channelAdmin) // Remove all permissions from the team admin role of the scheme
|
|
require.Nil(t, appErr)
|
|
|
|
team2 := th.CreateTeam()
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, team2.Id, th.BasicUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
channel := th.CreateChannel(th.Context, team2)
|
|
_, appErr = th.App.AddUserToChannel(th.Context, th.BasicUser, channel, true)
|
|
require.Nil(t, appErr)
|
|
session := model.Session{
|
|
Roles: model.SystemUserRoleId,
|
|
UserId: th.BasicUser.Id,
|
|
TeamMembers: []*model.TeamMember{
|
|
{
|
|
UserId: th.BasicUser.Id,
|
|
TeamId: team2.Id,
|
|
SchemeUser: true,
|
|
},
|
|
},
|
|
}
|
|
// ensure user can update channel properties before applying the scheme
|
|
require.True(t, th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties))
|
|
// apply the team scheme
|
|
team2.SchemeId = &team2Scheme.Id
|
|
_, appErr = th.App.UpdateTeamScheme(team2)
|
|
require.Nil(t, appErr)
|
|
require.False(t, th.App.SessionHasPermissionToChannel(th.Context, session, channel.Id, model.PermissionManagePublicChannelProperties))
|
|
}
|
|
|
|
func TestGetTeamMembers(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
var users []model.User
|
|
users = append(users, *th.BasicUser)
|
|
users = append(users, *th.BasicUser2)
|
|
|
|
for i := range 8 {
|
|
user := model.User{
|
|
Email: strings.ToLower(model.NewId()) + "success+test@example.com",
|
|
Username: fmt.Sprintf("user%v", i),
|
|
Password: "passwd1",
|
|
DeleteAt: int64(rand.Intn(2)),
|
|
}
|
|
ruser, err := th.App.CreateUser(th.Context, &user)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, ruser)
|
|
defer func() {
|
|
appErr := th.App.PermanentDeleteUser(th.Context, &user)
|
|
require.Nil(t, appErr)
|
|
}()
|
|
|
|
_, _, err = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
// Store the users for comparison later
|
|
users = append(users, *ruser)
|
|
}
|
|
|
|
t.Run("Ensure Sorted By Username when TeamMemberGet options is passed", func(t *testing.T) {
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME})
|
|
require.Nil(t, err)
|
|
|
|
// Sort the users array by username
|
|
sort.Slice(users, func(i, j int) bool {
|
|
return users[i].Username < users[j].Username
|
|
})
|
|
|
|
// We should have the same number of users in both users and members array as we have not excluded any deleted members
|
|
require.Equal(t, len(users), len(members))
|
|
for i, member := range members {
|
|
assert.Equal(t, users[i].Id, member.UserId)
|
|
}
|
|
})
|
|
|
|
t.Run("Ensure ExcludedDeletedUsers when TeamMemberGetOptions is passed", func(t *testing.T) {
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{ExcludeDeletedUsers: true})
|
|
require.Nil(t, err)
|
|
|
|
// Choose all users who aren't deleted from our users array
|
|
var usersNotDeletedIDs []string
|
|
var membersIDs []string
|
|
for _, u := range users {
|
|
if u.DeleteAt == 0 {
|
|
usersNotDeletedIDs = append(usersNotDeletedIDs, u.Id)
|
|
}
|
|
}
|
|
|
|
for _, m := range members {
|
|
membersIDs = append(membersIDs, m.UserId)
|
|
}
|
|
|
|
require.Equal(t, len(usersNotDeletedIDs), len(membersIDs))
|
|
require.ElementsMatch(t, usersNotDeletedIDs, membersIDs)
|
|
})
|
|
|
|
t.Run("Ensure Sorted By Username and ExcludedDeletedUsers when TeamMemberGetOptions is passed", func(t *testing.T) {
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 100, &model.TeamMembersGetOptions{Sort: model.USERNAME, ExcludeDeletedUsers: true})
|
|
require.Nil(t, err)
|
|
|
|
var usersNotDeleted []model.User
|
|
for _, u := range users {
|
|
if u.DeleteAt == 0 {
|
|
usersNotDeleted = append(usersNotDeleted, u)
|
|
}
|
|
}
|
|
|
|
// Sort our non deleted members by username
|
|
sort.Slice(usersNotDeleted, func(i, j int) bool {
|
|
return usersNotDeleted[i].Username < usersNotDeleted[j].Username
|
|
})
|
|
|
|
require.Equal(t, len(usersNotDeleted), len(members))
|
|
for i, member := range members {
|
|
assert.Equal(t, usersNotDeleted[i].Id, member.UserId)
|
|
}
|
|
})
|
|
|
|
t.Run("Ensure Sorted By User ID when no TeamMemberGetOptions is passed", func(t *testing.T) {
|
|
// Sort them by UserID because the result of GetTeamMembers() is also sorted
|
|
sort.Slice(users, func(i, j int) bool {
|
|
return users[i].Id < users[j].Id
|
|
})
|
|
|
|
// Fetch team members multiple times
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 5, nil)
|
|
require.Nil(t, err)
|
|
|
|
// This should return 5 members
|
|
members2, err := th.App.GetTeamMembers(th.BasicTeam.Id, 5, 6, nil)
|
|
require.Nil(t, err)
|
|
members = append(members, members2...)
|
|
|
|
require.Equal(t, len(users), len(members))
|
|
for i, member := range members {
|
|
assert.Equal(t, users[i].Id, member.UserId)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetTeamStats(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
t.Run("without view restrictions", func(t *testing.T) {
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, nil)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 5, nil)
|
|
require.Nil(t, err)
|
|
assert.Equal(t, int64(len(members)), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(len(members)), teamStats.ActiveMemberCount)
|
|
})
|
|
|
|
t.Run("with view restrictions by this team", func(t *testing.T) {
|
|
restrictions := &model.ViewUsersRestrictions{Teams: []string{th.BasicTeam.Id}}
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, restrictions)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
members, err := th.App.GetTeamMembers(th.BasicTeam.Id, 0, 5, nil)
|
|
require.Nil(t, err)
|
|
assert.Equal(t, int64(len(members)), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(len(members)), teamStats.ActiveMemberCount)
|
|
})
|
|
|
|
t.Run("with view restrictions by valid channel", func(t *testing.T) {
|
|
restrictions := &model.ViewUsersRestrictions{Teams: []string{}, Channels: []string{th.BasicChannel.Id}}
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, restrictions)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
members, err := th.App.GetChannelMembersPage(th.Context, th.BasicChannel.Id, 0, 5)
|
|
require.Nil(t, err)
|
|
assert.Equal(t, int64(len(members)), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(len(members)), teamStats.ActiveMemberCount)
|
|
})
|
|
|
|
t.Run("with view restrictions to not see anything", func(t *testing.T) {
|
|
restrictions := &model.ViewUsersRestrictions{Teams: []string{}, Channels: []string{}}
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, restrictions)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
assert.Equal(t, int64(0), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(0), teamStats.ActiveMemberCount)
|
|
})
|
|
|
|
t.Run("with view restrictions by other team", func(t *testing.T) {
|
|
restrictions := &model.ViewUsersRestrictions{Teams: []string{"other-team-id"}}
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, restrictions)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
assert.Equal(t, int64(0), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(0), teamStats.ActiveMemberCount)
|
|
})
|
|
|
|
t.Run("with view restrictions by not-existing channel", func(t *testing.T) {
|
|
restrictions := &model.ViewUsersRestrictions{Teams: []string{}, Channels: []string{"test"}}
|
|
teamStats, err := th.App.GetTeamStats(th.BasicTeam.Id, restrictions)
|
|
require.Nil(t, err)
|
|
require.NotNil(t, teamStats)
|
|
assert.Equal(t, int64(0), teamStats.TotalMemberCount)
|
|
assert.Equal(t, int64(0), teamStats.ActiveMemberCount)
|
|
})
|
|
}
|
|
|
|
func TestUpdateTeamMemberRolesChangingGuest(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
t.Run("from guest to user", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateGuest(th.Context, &user)
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, ruser.Id, "team_user")
|
|
require.NotNil(t, err, "Should fail when try to modify the guest role")
|
|
})
|
|
|
|
t.Run("from user to guest", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, ruser.Id, "team_guest")
|
|
require.NotNil(t, err, "Should fail when try to modify the guest role")
|
|
})
|
|
|
|
t.Run("from user to admin", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateUser(th.Context, &user)
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, ruser.Id, "team_user team_admin")
|
|
require.Nil(t, err, "Should work when you not modify guest role")
|
|
})
|
|
|
|
t.Run("from guest to guest plus custom", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateGuest(th.Context, &user)
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.CreateRole(&model.Role{Name: "custom", DisplayName: "custom", Description: "custom"})
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, ruser.Id, "team_guest custom")
|
|
require.Nil(t, err, "Should work when you not modify guest role")
|
|
})
|
|
|
|
t.Run("a guest cant have user role", func(t *testing.T) {
|
|
user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
|
|
ruser, _ := th.App.CreateGuest(th.Context, &user)
|
|
|
|
_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
|
|
require.Nil(t, err)
|
|
|
|
_, err = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, ruser.Id, "team_guest team_user")
|
|
require.NotNil(t, err, "Should work when you not modify guest role")
|
|
})
|
|
}
|
|
|
|
func TestInvalidateAllResendInviteEmailJobs(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
job, err := th.App.Srv().Jobs.CreateJob(th.Context, model.JobTypeResendInvitationEmail, map[string]string{})
|
|
require.Nil(t, err)
|
|
|
|
sysVar := &model.System{Name: job.Id, Value: "0"}
|
|
e := th.App.Srv().Store().System().SaveOrUpdate(sysVar)
|
|
require.NoError(t, e)
|
|
|
|
appErr := th.App.InvalidateAllResendInviteEmailJobs(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
j, e := th.App.Srv().Store().Job().Get(th.Context, job.Id)
|
|
require.NoError(t, e)
|
|
require.Equal(t, j.Status, model.JobStatusCanceled)
|
|
|
|
_, sysValErr := th.App.Srv().Store().System().GetByName(job.Id)
|
|
var errNotFound *store.ErrNotFound
|
|
require.ErrorAs(t, sysValErr, &errNotFound)
|
|
}
|
|
|
|
func TestInvalidateAllEmailInvites(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
t1 := model.Token{
|
|
Token: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
CreateAt: model.GetMillis(),
|
|
Type: TokenTypeGuestInvitation,
|
|
Extra: "",
|
|
}
|
|
err := th.App.Srv().Store().Token().Save(&t1)
|
|
require.NoError(t, err)
|
|
|
|
t2 := model.Token{
|
|
Token: "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
|
|
CreateAt: model.GetMillis(),
|
|
Type: TokenTypeTeamInvitation,
|
|
Extra: "",
|
|
}
|
|
err = th.App.Srv().Store().Token().Save(&t2)
|
|
require.NoError(t, err)
|
|
|
|
t3 := model.Token{
|
|
Token: "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
|
|
CreateAt: model.GetMillis(),
|
|
Type: "other",
|
|
Extra: "",
|
|
}
|
|
err = th.App.Srv().Store().Token().Save(&t3)
|
|
require.NoError(t, err)
|
|
|
|
appErr := th.App.InvalidateAllEmailInvites(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
_, err = th.App.Srv().Store().Token().GetByToken(t1.Token)
|
|
require.Error(t, err)
|
|
|
|
_, err = th.App.Srv().Store().Token().GetByToken(t2.Token)
|
|
require.Error(t, err)
|
|
|
|
_, err = th.App.Srv().Store().Token().GetByToken(t3.Token)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestClearTeamMembersCache(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := SetupWithStoreMock(t)
|
|
defer th.TearDown()
|
|
|
|
mockStore := th.App.Srv().Store().(*mocks.Store)
|
|
mockTeamStore := mocks.TeamStore{}
|
|
tms := []*model.TeamMember{}
|
|
for range 200 {
|
|
tms = append(tms, &model.TeamMember{
|
|
TeamId: "1",
|
|
})
|
|
}
|
|
mockTeamStore.On("GetMembers", "teamID", 0, 100, mock.Anything).Return(tms, nil)
|
|
mockTeamStore.On("GetMembers", "teamID", 100, 100, mock.Anything).Return([]*model.TeamMember{{
|
|
TeamId: "1",
|
|
}}, nil)
|
|
mockStore.On("Team").Return(&mockTeamStore)
|
|
mockStore.On("GetDBSchemaVersion").Return(1, nil)
|
|
|
|
require.NoError(t, th.App.ClearTeamMembersCache("teamID"))
|
|
}
|
|
|
|
func TestInviteNewUsersToTeamGracefully(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableEmailInvitations = true
|
|
})
|
|
|
|
t.Run("it return list of email with no error on success", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
memberInvite := &model.MemberInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
}
|
|
emailServiceMock.On("SendInviteEmails",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
memberInvite.Emails,
|
|
"",
|
|
mock.Anything,
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return(nil)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteNewUsersToTeamGracefully(th.Context, memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.Nil(t, res[0].Error)
|
|
})
|
|
|
|
t.Run("it should assign errors to emails when failing to send", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
memberInvite := &model.MemberInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
}
|
|
emailServiceMock.On("SendInviteEmails",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
memberInvite.Emails,
|
|
"",
|
|
mock.Anything,
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return(email.SendMailError)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteNewUsersToTeamGracefully(th.Context, memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.NotNil(t, res[0].Error)
|
|
})
|
|
|
|
t.Run("it return list of email with no error when inviting to team and channels using memberInvite struct", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
memberInvite := &model.MemberInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
ChannelIds: []string{th.BasicChannel.Id},
|
|
}
|
|
emailServiceMock.On("SendInviteEmailsToTeamAndChannels",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("[]*model.Channel"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("[]uint8"),
|
|
memberInvite.Emails,
|
|
"",
|
|
mock.Anything,
|
|
mock.AnythingOfType("string"),
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return([]*model.EmailInviteWithError{}, nil)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteNewUsersToTeamGracefully(th.Context, memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.Nil(t, res[0].Error)
|
|
})
|
|
|
|
t.Run("it return list of email with no error when inviting to team and channels using plain emails array", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
memberInvite := &model.MemberInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
}
|
|
emailServiceMock.On("SendInviteEmails",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
[]string{"idontexist@mattermost.com"},
|
|
"",
|
|
mock.Anything,
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return(nil)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteNewUsersToTeamGracefully(th.Context, memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.Nil(t, res[0].Error)
|
|
})
|
|
}
|
|
|
|
func TestInviteGuestsToChannelsGracefully(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableEmailInvitations = true
|
|
})
|
|
|
|
t.Run("it return list of email with no error on success", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
emailServiceMock.On("SendGuestInviteEmails",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("[]*model.Channel"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("[]uint8"),
|
|
[]string{"idontexist@mattermost.com"},
|
|
"",
|
|
"",
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return(nil)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteGuestsToChannelsGracefully(th.Context, th.BasicTeam.Id, &model.GuestsInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
Channels: []string{th.BasicChannel.Id},
|
|
}, th.BasicUser.Id)
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.Nil(t, res[0].Error)
|
|
})
|
|
|
|
t.Run("it should assign errors to emails when failing to send", func(t *testing.T) {
|
|
emailServiceMock := emailmocks.ServiceInterface{}
|
|
emailServiceMock.On("SendGuestInviteEmails",
|
|
mock.AnythingOfType("*model.Team"),
|
|
mock.AnythingOfType("[]*model.Channel"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("string"),
|
|
mock.AnythingOfType("[]uint8"),
|
|
[]string{"idontexist@mattermost.com"},
|
|
"",
|
|
"",
|
|
true,
|
|
false,
|
|
false,
|
|
).Once().Return(email.SendMailError)
|
|
emailServiceMock.On("Stop").Once().Return()
|
|
th.App.Srv().EmailService = &emailServiceMock
|
|
|
|
res, err := th.App.InviteGuestsToChannelsGracefully(th.Context, th.BasicTeam.Id, &model.GuestsInvite{
|
|
Emails: []string{"idontexist@mattermost.com"},
|
|
Channels: []string{th.BasicChannel.Id},
|
|
}, th.BasicUser.Id)
|
|
|
|
require.Nil(t, err)
|
|
require.Len(t, res, 1)
|
|
require.NotNil(t, res[0].Error)
|
|
})
|
|
}
|
|
|
|
func TestInviteGuestsToChannelsWithPolicyEnforced(t *testing.T) {
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.ServiceSettings.EnableEmailInvitations = true
|
|
})
|
|
|
|
// Create a private channel
|
|
channel := th.CreatePrivateChannel(th.Context, th.BasicTeam)
|
|
|
|
// Create a policy with the same ID as the channel
|
|
channelPolicy := &model.AccessControlPolicy{
|
|
Type: model.AccessControlPolicyTypeChannel,
|
|
ID: channel.Id, // Use the channel ID directly
|
|
Name: "Test Channel Policy",
|
|
Revision: 1,
|
|
Version: model.AccessControlPolicyVersionV0_2,
|
|
Rules: []model.AccessControlPolicyRule{
|
|
{
|
|
Actions: []string{"view", "join_channel"},
|
|
Expression: "user.attributes.program == \"test-program\"",
|
|
},
|
|
},
|
|
}
|
|
|
|
// Save the channel policy
|
|
channelPolicy, err := th.App.Srv().Store().AccessControlPolicy().Save(th.Context, channelPolicy)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, channelPolicy)
|
|
|
|
// Attempt to invite guests to the policy-enforced channel
|
|
guestsInvite := &model.GuestsInvite{
|
|
Emails: []string{"guest@example.com"},
|
|
Channels: []string{channel.Id},
|
|
Message: "test message",
|
|
}
|
|
|
|
// Call the function we want to test
|
|
_, _, _, appErr := th.App.prepareInviteGuestsToChannels(th.BasicTeam.Id, guestsInvite, th.BasicUser.Id)
|
|
|
|
// Verify that the appropriate error is returned
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "api.team.invite_guests.policy_enforced_channel.app_error", appErr.Id)
|
|
}
|
|
|
|
func TestTeamSendEvents(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
testCluster := &testlib.FakeClusterInterface{}
|
|
th.Server.Platform().SetCluster(testCluster)
|
|
defer th.Server.Platform().SetCluster(nil)
|
|
|
|
team := th.CreateTeam()
|
|
|
|
testCluster.ClearMessages()
|
|
|
|
wsEvents := []model.WebsocketEventType{model.WebsocketEventUpdateTeam, model.WebsocketEventRestoreTeam, model.WebsocketEventDeleteTeam}
|
|
for _, wsEvent := range wsEvents {
|
|
appErr := th.App.sendTeamEvent(team, wsEvent)
|
|
require.Nil(t, appErr)
|
|
}
|
|
|
|
msgs := testCluster.GetMessages()
|
|
require.Len(t, msgs, len(wsEvents))
|
|
|
|
for _, msg := range msgs {
|
|
ev, err := model.WebSocketEventFromJSON(bytes.NewReader(msg.Data))
|
|
require.NoError(t, err)
|
|
|
|
// The event should be a team event.
|
|
require.Equal(t, team.Id, ev.GetBroadcast().TeamId)
|
|
|
|
// Make sure we're hiding the sensitive fields.
|
|
var teamFromEvent *model.Team
|
|
err = json.Unmarshal([]byte(ev.GetData()["team"].(string)), &teamFromEvent)
|
|
require.NoError(t, err)
|
|
require.Equal(t, team.Id, teamFromEvent.Id)
|
|
require.Equal(t, team.DisplayName, teamFromEvent.DisplayName)
|
|
require.Equal(t, team.Name, teamFromEvent.Name)
|
|
require.Equal(t, team.Description, teamFromEvent.Description)
|
|
require.Equal(t, "", teamFromEvent.Email)
|
|
require.Equal(t, "", teamFromEvent.InviteId)
|
|
}
|
|
}
|