Files
mostlymatter/app/user_test.go
Agniva De Sarker f0ecdcc5f5 Move Channels into App (#18623)
* Move Channels into App

In this PR, we make Channels as part of App
instead of Server. This is part of the transition period
of moving fields from Server to Channels.

For now, Channels contains Server. So the hierarchy is

App -> Channels -> Server.

And as a first step, we also move httpService to Channels.

```release-note
NONE
```

* Fixing another test

```release-note
NONE
```

* new method

```release-note
NONE
```
2021-10-12 11:39:49 +05:30

1611 строки
54 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"
"os"
"strings"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"github.com/mattermost/mattermost-server/v6/app/request"
"github.com/mattermost/mattermost-server/v6/app/users"
"github.com/mattermost/mattermost-server/v6/einterfaces"
"github.com/mattermost/mattermost-server/v6/einterfaces/mocks"
"github.com/mattermost/mattermost-server/v6/model"
oauthgitlab "github.com/mattermost/mattermost-server/v6/model/gitlab"
"github.com/mattermost/mattermost-server/v6/store"
storemocks "github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
"github.com/mattermost/mattermost-server/v6/utils/testutils"
)
func TestCreateOAuthUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GitLabSettings.Enable = true
})
t.Run("create user successfully", func(t *testing.T) {
glUser := oauthgitlab.GitLabUser{Id: 42, Username: "o" + model.NewId(), Email: model.NewId() + "@simulator.amazonses.com", Name: "Joram Wilander"}
js, jsonErr := json.Marshal(glUser)
require.NoError(t, jsonErr)
user, err := th.App.CreateOAuthUser(th.Context, model.UserAuthServiceGitlab, bytes.NewReader(js), th.BasicTeam.Id, nil)
require.Nil(t, err)
require.Equal(t, glUser.Username, user.Username, "usernames didn't match")
th.App.PermanentDeleteUser(th.Context, user)
})
t.Run("user exists, update authdata successfully", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.Office365Settings.Enable = true
})
dbUser := th.BasicUser
// mock oAuth Provider, return data
mockUser := &model.User{Id: "abcdef", AuthData: model.NewString("e7110007-64be-43d8-9840-4a7e9c26b710"), Email: dbUser.Email}
providerMock := &mocks.OAuthProvider{}
providerMock.On("IsSameUser", mock.Anything, mock.Anything).Return(true)
providerMock.On("GetUserFromJSON", mock.Anything, mock.Anything).Return(mockUser, nil)
einterfaces.RegisterOAuthProvider(model.ServiceOffice365, providerMock)
// Update user to be OAuth, formatting to match Office365 OAuth data
s, er2 := th.App.Srv().Store.User().UpdateAuthData(dbUser.Id, model.ServiceOffice365, model.NewString("e711000764be43d898404a7e9c26b710"), "", false)
assert.NoError(t, er2)
assert.Equal(t, dbUser.Id, s)
// data passed doesn't matter as return is mocked
_, err := th.App.CreateOAuthUser(th.Context, model.ServiceOffice365, strings.NewReader("{}"), th.BasicTeam.Id, nil)
assert.Nil(t, err)
u, er := th.App.Srv().Store.User().GetByEmail(dbUser.Email)
assert.NoError(t, er)
// make sure authdata is updated
assert.Equal(t, "e7110007-64be-43d8-9840-4a7e9c26b710", *u.AuthData)
})
t.Run("user creation disabled", func(t *testing.T) {
*th.App.Config().TeamSettings.EnableUserCreation = false
_, err := th.App.CreateOAuthUser(th.Context, model.UserAuthServiceGitlab, strings.NewReader("{}"), th.BasicTeam.Id, nil)
require.NotNil(t, err, "should have failed - user creation disabled")
})
}
func TestSetDefaultProfileImage(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
err := th.App.SetDefaultProfileImage(&model.User{
Id: model.NewId(),
Username: "notvaliduser",
})
// It doesn't fail, but it does nothing
require.Nil(t, err)
user := th.BasicUser
err = th.App.SetDefaultProfileImage(user)
require.Nil(t, err)
user = getUserFromDB(th.App, user.Id, t)
assert.Equal(t, int64(0), user.LastPictureUpdate)
}
func TestAdjustProfileImage(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
_, err := th.App.AdjustImage(bytes.NewReader([]byte{}))
require.NotNil(t, err)
// test image isn't the correct dimensions
// it should be adjusted
testjpg, error := testutils.ReadTestFile("testjpg.jpg")
require.NoError(t, error)
adjusted, err := th.App.AdjustImage(bytes.NewReader(testjpg))
require.Nil(t, err)
assert.True(t, adjusted.Len() > 0)
assert.NotEqual(t, testjpg, adjusted)
// default image should require adjustement
user := th.BasicUser
image, err := th.App.GetDefaultProfileImage(user)
require.Nil(t, err)
image2, err := th.App.AdjustImage(bytes.NewReader(image))
require.Nil(t, err)
assert.Equal(t, image, image2.Bytes())
}
func TestUpdateUserToRestrictedDomain(t *testing.T) {
th := Setup(t)
defer th.TearDown()
user := th.CreateUser()
defer th.App.PermanentDeleteUser(th.Context, user)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictCreationToDomains = "foo.com"
})
_, err := th.App.UpdateUser(user, false)
assert.Nil(t, err)
user.Email = "asdf@ghjk.l"
_, err = th.App.UpdateUser(user, false)
assert.NotNil(t, err)
t.Run("Restricted Domains must be ignored for guest users", func(t *testing.T) {
guest := th.CreateGuest()
defer th.App.PermanentDeleteUser(th.Context, guest)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.RestrictCreationToDomains = "foo.com"
})
guest.Email = "asdf@bar.com"
updatedGuest, err := th.App.UpdateUser(guest, false)
require.Nil(t, err)
require.Equal(t, guest.Email, updatedGuest.Email)
})
t.Run("Guest users should be affected by guest restricted domains", func(t *testing.T) {
guest := th.CreateGuest()
defer th.App.PermanentDeleteUser(th.Context, guest)
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GuestAccountsSettings.RestrictCreationToDomains = "foo.com"
})
guest.Email = "asdf@bar.com"
_, err := th.App.UpdateUser(guest, false)
require.NotNil(t, err)
guest.Email = "asdf@foo.com"
updatedGuest, err := th.App.UpdateUser(guest, false)
require.Nil(t, err)
require.Equal(t, guest.Email, updatedGuest.Email)
})
}
func TestUpdateUserActive(t *testing.T) {
th := Setup(t)
defer th.TearDown()
user := th.CreateUser()
EnableUserDeactivation := th.App.Config().TeamSettings.EnableUserDeactivation
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) { cfg.TeamSettings.EnableUserDeactivation = EnableUserDeactivation })
}()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.TeamSettings.EnableUserDeactivation = true
})
err := th.App.UpdateUserActive(th.Context, user.Id, false)
assert.Nil(t, err)
}
func TestUpdateActiveBotsSideEffect(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
bot, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "username",
Description: "a bot",
OwnerId: th.BasicUser.Id,
})
require.Nil(t, err)
defer th.App.PermanentDeleteBot(bot.UserId)
// Automatic deactivation disabled
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.DisableBotsWhenOwnerIsDeactivated = false
})
th.App.UpdateActive(th.Context, th.BasicUser, false)
retbot1, err := th.App.GetBot(bot.UserId, true)
require.Nil(t, err)
require.Zero(t, retbot1.DeleteAt)
user1, err := th.App.GetUser(bot.UserId)
require.Nil(t, err)
require.Zero(t, user1.DeleteAt)
th.App.UpdateActive(th.Context, th.BasicUser, true)
// Automatic deactivation enabled
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.DisableBotsWhenOwnerIsDeactivated = true
})
th.App.UpdateActive(th.Context, th.BasicUser, false)
retbot2, err := th.App.GetBot(bot.UserId, true)
require.Nil(t, err)
require.NotZero(t, retbot2.DeleteAt)
user2, err := th.App.GetUser(bot.UserId)
require.Nil(t, err)
require.NotZero(t, user2.DeleteAt)
th.App.UpdateActive(th.Context, th.BasicUser, true)
}
func TestUpdateOAuthUserAttrs(t *testing.T) {
th := Setup(t)
defer th.TearDown()
id := model.NewId()
id2 := model.NewId()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.GitLabSettings.Enable = true
})
gitlabProvider := einterfaces.GetOAuthProvider("gitlab")
username := "user" + id
username2 := "user" + id2
email := "user" + id + "@nowhere.com"
email2 := "user" + id2 + "@nowhere.com"
var user, user2 *model.User
var gitlabUserObj oauthgitlab.GitLabUser
user, gitlabUserObj = createGitlabUser(t, th.App, th.Context, 1, username, email)
user2, _ = createGitlabUser(t, th.App, th.Context, 2, username2, email2)
t.Run("UpdateUsername", func(t *testing.T) {
t.Run("NoExistingUserWithSameUsername", func(t *testing.T) {
gitlabUserObj.Username = "updateduser" + model.NewId()
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, gitlabUserObj.Username, user.Username, "user's username is not updated")
})
t.Run("ExistinguserWithSameUsername", func(t *testing.T) {
gitlabUserObj.Username = user2.Username
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.NotEqual(t, gitlabUserObj.Username, user.Username, "user's username is updated though there already exists another user with the same username")
})
})
t.Run("UpdateEmail", func(t *testing.T) {
t.Run("NoExistingUserWithSameEmail", func(t *testing.T) {
gitlabUserObj.Email = "newuser" + model.NewId() + "@nowhere.com"
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, gitlabUserObj.Email, user.Email, "user's email is not updated")
require.True(t, user.EmailVerified, "user's email should have been verified")
})
t.Run("ExistingUserWithSameEmail", func(t *testing.T) {
gitlabUserObj.Email = user2.Email
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.NotEqual(t, gitlabUserObj.Email, user.Email, "user's email is updated though there already exists another user with the same email")
})
})
t.Run("UpdateFirstName", func(t *testing.T) {
gitlabUserObj.Name = "Updated User"
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, "Updated", user.FirstName, "user's first name is not updated")
})
t.Run("UpdateLastName", func(t *testing.T) {
gitlabUserObj.Name = "Updated Lastname"
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
data := bytes.NewReader(gitlabUser)
user = getUserFromDB(th.App, user.Id, t)
th.App.UpdateOAuthUserAttrs(data, user, gitlabProvider, "gitlab", nil)
user = getUserFromDB(th.App, user.Id, t)
require.Equal(t, "Lastname", user.LastName, "user's last name is not updated")
})
}
func TestCreateUserConflict(t *testing.T) {
th := Setup(t)
defer th.TearDown()
user := &model.User{
Email: "test@localhost",
Username: model.NewId(),
}
user, err := th.App.Srv().Store.User().Save(user)
require.NoError(t, err)
username := user.Username
var invErr *store.ErrInvalidInput
// Same id
_, err = th.App.Srv().Store.User().Save(user)
require.Error(t, err)
require.True(t, errors.As(err, &invErr))
assert.Equal(t, "id", invErr.Field)
// Same email
user = &model.User{
Email: "test@localhost",
Username: model.NewId(),
}
_, err = th.App.Srv().Store.User().Save(user)
require.Error(t, err)
require.True(t, errors.As(err, &invErr))
assert.Equal(t, "email", invErr.Field)
// Same username
user = &model.User{
Email: "test2@localhost",
Username: username,
}
_, err = th.App.Srv().Store.User().Save(user)
require.Error(t, err)
require.True(t, errors.As(err, &invErr))
assert.Equal(t, "username", invErr.Field)
}
func TestUpdateUserEmail(t *testing.T) {
th := Setup(t)
defer th.TearDown()
user := th.CreateUser()
t.Run("RequireVerification", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = true
})
currentEmail := user.Email
newEmail := th.MakeEmail()
user.Email = newEmail
user2, appErr := th.App.UpdateUser(user, false)
assert.Nil(t, appErr)
assert.Equal(t, currentEmail, user2.Email)
assert.True(t, user2.EmailVerified)
token, err := th.App.Srv().EmailService.CreateVerifyEmailToken(user2.Id, newEmail)
assert.NoError(t, err)
appErr = th.App.VerifyEmailFromToken(token.Token)
assert.Nil(t, appErr)
user2, appErr = th.App.GetUser(user2.Id)
assert.Nil(t, appErr)
assert.Equal(t, newEmail, user2.Email)
assert.True(t, user2.EmailVerified)
// Create bot user
botuser := model.User{
Email: "botuser@localhost",
Username: model.NewId(),
IsBot: true,
}
_, nErr := th.App.Srv().Store.User().Save(&botuser)
assert.NoError(t, nErr)
newBotEmail := th.MakeEmail()
botuser.Email = newBotEmail
botuser2, appErr := th.App.UpdateUser(&botuser, false)
assert.Nil(t, appErr)
assert.Equal(t, botuser2.Email, newBotEmail)
})
t.Run("RequireVerificationAlreadyUsedEmail", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = true
})
user2 := th.CreateUser()
newEmail := user2.Email
user.Email = newEmail
user3, err := th.App.UpdateUser(user, false)
require.NotNil(t, err)
assert.Equal(t, err.Id, "app.user.save.email_exists.app_error")
assert.Nil(t, user3)
})
t.Run("NoVerification", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = false
})
newEmail := th.MakeEmail()
user.Email = newEmail
user2, err := th.App.UpdateUser(user, false)
assert.Nil(t, err)
assert.Equal(t, newEmail, user2.Email)
// Create bot user
botuser := model.User{
Email: "botuser@localhost",
Username: model.NewId(),
IsBot: true,
}
_, nErr := th.App.Srv().Store.User().Save(&botuser)
assert.NoError(t, nErr)
newBotEmail := th.MakeEmail()
botuser.Email = newBotEmail
botuser2, err := th.App.UpdateUser(&botuser, false)
assert.Nil(t, err)
assert.Equal(t, botuser2.Email, newBotEmail)
})
t.Run("NoVerificationAlreadyUsedEmail", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = false
})
user2 := th.CreateUser()
newEmail := user2.Email
user.Email = newEmail
user3, err := th.App.UpdateUser(user, false)
require.NotNil(t, err)
assert.Equal(t, err.Id, "app.user.save.email_exists.app_error")
assert.Nil(t, user3)
})
t.Run("Only the last token works if verification is required", func(t *testing.T) {
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.EmailSettings.RequireEmailVerification = true
})
// we update the email a first time and update. The first
// token is sent with the email
user.Email = th.MakeEmail()
_, appErr := th.App.UpdateUser(user, true)
require.Nil(t, appErr)
tokens := []*model.Token{}
require.Eventually(t, func() bool {
var err error
tokens, err = th.App.Srv().Store.Token().GetAllTokensByType(TokenTypeVerifyEmail)
return err == nil && len(tokens) == 1
}, 100*time.Millisecond, 10*time.Millisecond)
firstToken := tokens[0]
// without using the first token, we update the email a second
// time and another token gets sent. The first one should not
// work anymore and the second should work properly
user.Email = th.MakeEmail()
_, appErr = th.App.UpdateUser(user, true)
require.Nil(t, appErr)
require.Eventually(t, func() bool {
var err error
tokens, err = th.App.Srv().Store.Token().GetAllTokensByType(TokenTypeVerifyEmail)
return err == nil && len(tokens) == 1
}, 100*time.Millisecond, 10*time.Millisecond)
secondToken := tokens[0]
_, err := th.App.Srv().Store.Token().GetByToken(firstToken.Token)
require.Error(t, err)
require.NotNil(t, th.App.VerifyEmailFromToken(firstToken.Token))
require.Nil(t, th.App.VerifyEmailFromToken(secondToken.Token))
require.NotNil(t, th.App.VerifyEmailFromToken(firstToken.Token))
})
}
func getUserFromDB(a *App, id string, t *testing.T) *model.User {
user, err := a.GetUser(id)
require.Nil(t, err, "user is not found", err)
return user
}
func getGitlabUserPayload(gitlabUser oauthgitlab.GitLabUser, t *testing.T) []byte {
var payload []byte
var err error
payload, err = json.Marshal(gitlabUser)
require.NoError(t, err, "Serialization of gitlab user to json failed", err)
return payload
}
func createGitlabUser(t *testing.T, a *App, c *request.Context, id int64, username string, email string) (*model.User, oauthgitlab.GitLabUser) {
gitlabUserObj := oauthgitlab.GitLabUser{Id: id, Username: username, Login: "user1", Email: email, Name: "Test User"}
gitlabUser := getGitlabUserPayload(gitlabUserObj, t)
var user *model.User
var err *model.AppError
user, err = a.CreateOAuthUser(c, "gitlab", bytes.NewReader(gitlabUser), "", nil)
require.Nil(t, err, "unable to create the user", err)
return user, gitlabUserObj
}
func TestGetUsersByStatus(t *testing.T) {
th := Setup(t)
defer th.TearDown()
team := th.CreateTeam()
channel, err := th.App.CreateChannel(th.Context, &model.Channel{
DisplayName: "dn_" + model.NewId(),
Name: "name_" + model.NewId(),
Type: model.ChannelTypeOpen,
TeamId: team.Id,
CreatorId: model.NewId(),
}, false)
require.Nil(t, err, "failed to create channel: %v", err)
createUserWithStatus := func(username string, status string) *model.User {
id := model.NewId()
user, err := th.App.CreateUser(th.Context, &model.User{
Email: "success+" + id + "@simulator.amazonses.com",
Username: "un_" + username + "_" + id,
Nickname: "nn_" + id,
Password: "Password1",
})
require.Nil(t, err, "failed to create user: %v", err)
th.LinkUserToTeam(user, team)
th.AddUserToChannel(user, channel)
th.App.SaveAndBroadcastStatus(&model.Status{
UserId: user.Id,
Status: status,
Manual: true,
})
return user
}
// Creating these out of order in case that affects results
awayUser1 := createUserWithStatus("away1", model.StatusAway)
awayUser2 := createUserWithStatus("away2", model.StatusAway)
dndUser1 := createUserWithStatus("dnd1", model.StatusDnd)
dndUser2 := createUserWithStatus("dnd2", model.StatusDnd)
offlineUser1 := createUserWithStatus("offline1", model.StatusOffline)
offlineUser2 := createUserWithStatus("offline2", model.StatusOffline)
onlineUser1 := createUserWithStatus("online1", model.StatusOnline)
onlineUser2 := createUserWithStatus("online2", model.StatusOnline)
t.Run("sorting by status then alphabetical", func(t *testing.T) {
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(&model.UserGetOptions{
InChannelId: channel.Id,
Page: 0,
PerPage: 8,
}, true)
require.Nil(t, err)
expectedUsersByStatus := []*model.User{
onlineUser1,
onlineUser2,
awayUser1,
awayUser2,
dndUser1,
dndUser2,
offlineUser1,
offlineUser2,
}
require.Equalf(t, len(expectedUsersByStatus), len(usersByStatus), "received only %v users, expected %v", len(usersByStatus), len(expectedUsersByStatus))
for i := range usersByStatus {
require.Equalf(t, expectedUsersByStatus[i].Id, usersByStatus[i].Id, "received user %v at index %v, expected %v", usersByStatus[i].Username, i, expectedUsersByStatus[i].Username)
}
})
t.Run("paging", func(t *testing.T) {
usersByStatus, err := th.App.GetUsersInChannelPageByStatus(&model.UserGetOptions{
InChannelId: channel.Id,
Page: 0,
PerPage: 3,
}, true)
require.Nil(t, err)
require.Equal(t, 3, len(usersByStatus), "received too many users")
require.False(
t,
usersByStatus[0].Id != onlineUser1.Id && usersByStatus[1].Id != onlineUser2.Id,
"expected to receive online users first",
)
require.Equal(t, awayUser1.Id, usersByStatus[2].Id, "expected to receive away users second")
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(&model.UserGetOptions{
InChannelId: channel.Id,
Page: 1,
PerPage: 3,
}, true)
require.Nil(t, err)
require.NotEmpty(t, usersByStatus, "at least some users are expected")
require.Equal(t, awayUser2.Id, usersByStatus[0].Id, "expected to receive away users second")
require.False(
t,
usersByStatus[1].Id != dndUser1.Id && usersByStatus[2].Id != dndUser2.Id,
"expected to receive dnd users third",
)
usersByStatus, err = th.App.GetUsersInChannelPageByStatus(&model.UserGetOptions{
InChannelId: channel.Id,
Page: 1,
PerPage: 4,
}, true)
require.Nil(t, err)
require.Equal(t, 4, len(usersByStatus), "received too many users")
require.False(
t,
usersByStatus[0].Id != dndUser1.Id && usersByStatus[1].Id != dndUser2.Id,
"expected to receive dnd users third",
)
require.False(
t,
usersByStatus[2].Id != offlineUser1.Id && usersByStatus[3].Id != offlineUser2.Id,
"expected to receive offline users last",
)
})
}
func TestCreateUserWithInviteId(t *testing.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: ""}
t.Run("should create a user", func(t *testing.T) {
u, err := th.App.CreateUserWithInviteId(th.Context, &user, th.BasicTeam.InviteId, "")
require.Nil(t, err)
require.Equal(t, u.Id, user.Id)
})
t.Run("invalid invite id", func(t *testing.T) {
_, err := th.App.CreateUserWithInviteId(th.Context, &user, "", "")
require.NotNil(t, err)
require.Contains(t, err.Id, "app.team.get_by_invite_id")
})
t.Run("invalid domain", func(t *testing.T) {
th.BasicTeam.AllowedDomains = "mattermost.com"
_, nErr := th.App.Srv().Store.Team().Update(th.BasicTeam)
require.NoError(t, nErr)
_, err := th.App.CreateUserWithInviteId(th.Context, &user, th.BasicTeam.InviteId, "")
require.NotNil(t, err)
require.Equal(t, "api.team.invite_members.invalid_email.app_error", err.Id)
})
}
func TestCreateUserWithToken(t *testing.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: ""}
t.Run("invalid token", func(t *testing.T) {
_, err := th.App.CreateUserWithToken(th.Context, &user, &model.Token{Token: "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, "email": user.Email}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(token))
defer th.App.DeleteToken(token)
_, err := th.App.CreateUserWithToken(th.Context, &user, 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, "email": user.Email}),
)
token.CreateAt = model.GetMillis() - InvitationExpiryTime - 1
require.NoError(t, th.App.Srv().Store.Token().Save(token))
defer th.App.DeleteToken(token)
_, err := th.App.CreateUserWithToken(th.Context, &user, 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(), "email": user.Email}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(token))
defer th.App.DeleteToken(token)
_, err := th.App.CreateUserWithToken(th.Context, &user, token)
require.NotNil(t, err, "Should fail on bad team id")
})
t.Run("valid regular user request", func(t *testing.T) {
invitationEmail := model.NewId() + "other-email@test.com"
token := model.NewToken(
TokenTypeTeamInvitation,
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "email": invitationEmail}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(token))
newUser, err := th.App.CreateUserWithToken(th.Context, &user, token)
require.Nil(t, err, "Should add user to the team. err=%v", err)
assert.False(t, newUser.IsGuest())
require.Equal(t, invitationEmail, newUser.Email, "The user email must be the invitation one")
_, 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.BasicTeam.Id, newUser.Id)
require.Nil(t, err)
assert.Len(t, members, 2)
})
t.Run("valid guest request", func(t *testing.T) {
invitationEmail := model.NewId() + "other-email@test.com"
token := model.NewToken(
TokenTypeGuestInvitation,
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "email": invitationEmail, "channels": th.BasicChannel.Id}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(token))
guest := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
newGuest, err := th.App.CreateUserWithToken(th.Context, &guest, token)
require.Nil(t, err, "Should add user to the team. err=%v", err)
assert.True(t, newGuest.IsGuest())
require.Equal(t, invitationEmail, newGuest.Email, "The user email must be the invitation one")
_, 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.BasicTeam.Id, newGuest.Id)
require.Nil(t, err)
require.Len(t, members, 1)
assert.Equal(t, members[0].ChannelId, th.BasicChannel.Id)
})
t.Run("create guest having email domain restrictions", func(t *testing.T) {
enableGuestDomainRestricions := *th.App.Config().GuestAccountsSettings.RestrictCreationToDomains
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.GuestAccountsSettings.RestrictCreationToDomains = &enableGuestDomainRestricions
})
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.GuestAccountsSettings.RestrictCreationToDomains = "restricted.com" })
forbiddenInvitationEmail := model.NewId() + "other-email@test.com"
grantedInvitationEmail := model.NewId() + "other-email@restricted.com"
forbiddenDomainToken := model.NewToken(
TokenTypeGuestInvitation,
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "email": forbiddenInvitationEmail, "channels": th.BasicChannel.Id}),
)
grantedDomainToken := model.NewToken(
TokenTypeGuestInvitation,
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "email": grantedInvitationEmail, "channels": th.BasicChannel.Id}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(forbiddenDomainToken))
require.NoError(t, th.App.Srv().Store.Token().Save(grantedDomainToken))
guest := model.User{
Email: strings.ToLower(model.NewId()) + "+test@example.com",
Nickname: "Darth Vader",
Username: "vader" + model.NewId(),
Password: "passwd1",
AuthService: "",
}
newGuest, err := th.App.CreateUserWithToken(th.Context, &guest, forbiddenDomainToken)
require.NotNil(t, err)
require.Nil(t, newGuest)
assert.Equal(t, "api.user.create_user.accepted_domain.app_error", err.Id)
newGuest, err = th.App.CreateUserWithToken(th.Context, &guest, grantedDomainToken)
require.Nil(t, err)
assert.True(t, newGuest.IsGuest())
require.Equal(t, grantedInvitationEmail, newGuest.Email)
_, nErr := th.App.Srv().Store.Token().GetByToken(grantedDomainToken.Token)
require.Error(t, nErr)
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, newGuest.Id)
require.Nil(t, err)
require.Len(t, members, 1)
assert.Equal(t, members[0].ChannelId, th.BasicChannel.Id)
})
t.Run("create guest having team and system email domain restrictions", func(t *testing.T) {
th.BasicTeam.AllowedDomains = "restricted-team.com"
_, err := th.App.UpdateTeam(th.BasicTeam)
require.Nil(t, err, "Should update the team")
enableGuestDomainRestricions := *th.App.Config().TeamSettings.RestrictCreationToDomains
defer func() {
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.TeamSettings.RestrictCreationToDomains = &enableGuestDomainRestricions
})
}()
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictCreationToDomains = "restricted.com" })
invitationEmail := model.NewId() + "other-email@test.com"
token := model.NewToken(
TokenTypeGuestInvitation,
model.MapToJSON(map[string]string{"teamId": th.BasicTeam.Id, "email": invitationEmail, "channels": th.BasicChannel.Id}),
)
require.NoError(t, th.App.Srv().Store.Token().Save(token))
guest := model.User{
Email: strings.ToLower(model.NewId()) + "+test@example.com",
Nickname: "Darth Vader",
Username: "vader" + model.NewId(),
Password: "passwd1",
AuthService: "",
}
newGuest, err := th.App.CreateUserWithToken(th.Context, &guest, token)
require.Nil(t, err)
assert.True(t, newGuest.IsGuest())
assert.Equal(t, invitationEmail, newGuest.Email, "The user email must be the invitation one")
_, nErr := th.App.Srv().Store.Token().GetByToken(token.Token)
require.Error(t, nErr)
members, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, newGuest.Id)
require.Nil(t, err)
require.Len(t, members, 1)
assert.Equal(t, members[0].ChannelId, th.BasicChannel.Id)
})
}
func TestPermanentDeleteUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
b := []byte("testimage")
finfo, err := th.App.DoUploadFile(th.Context, time.Now(), th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, "testfile.txt", b)
require.Nil(t, err, "Unable to upload file. err=%v", err)
bot, err := th.App.CreateBot(th.Context, &model.Bot{
Username: "botname",
Description: "a bot",
OwnerId: model.NewId(),
})
assert.Nil(t, err)
var bots1 []*model.Bot
var bots2 []*model.Bot
sqlStore := mainHelper.GetSQLStore()
_, err1 := sqlStore.GetMaster().Select(&bots1, "SELECT * FROM Bots")
assert.NoError(t, err1)
assert.Equal(t, 1, len(bots1))
// test that bot is deleted from bots table
retUser1, err := th.App.GetUser(bot.UserId)
assert.Nil(t, err)
err = th.App.PermanentDeleteUser(th.Context, retUser1)
assert.Nil(t, err)
_, err1 = sqlStore.GetMaster().Select(&bots2, "SELECT * FROM Bots")
assert.NoError(t, err1)
assert.Equal(t, 0, len(bots2))
err = th.App.PermanentDeleteUser(th.Context, th.BasicUser)
require.Nil(t, err, "Unable to delete user. err=%v", err)
res, err := th.App.FileExists(finfo.Path)
require.Nil(t, err, "Unable to check whether file exists. err=%v", err)
require.False(t, res, "File was not deleted on FS. err=%v", err)
finfo, err = th.App.GetFileInfo(finfo.Id)
require.Nil(t, finfo, "Unable to find finfo. err=%v", err)
require.NotNil(t, err, "GetFileInfo after DeleteUser is nil. err=%v", err)
}
func TestPasswordRecovery(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
token, err := th.App.CreatePasswordRecoveryToken(th.BasicUser.Id, th.BasicUser.Email)
assert.Nil(t, err)
tokenData := struct {
UserId string
Email string
}{}
err2 := json.Unmarshal([]byte(token.Extra), &tokenData)
assert.NoError(t, err2)
assert.Equal(t, th.BasicUser.Id, tokenData.UserId)
assert.Equal(t, th.BasicUser.Email, tokenData.Email)
// Password token with same eMail as during creation
err = th.App.ResetPasswordFromToken(token.Token, "abcdefgh")
assert.Nil(t, err)
// Password token with modified eMail after creation
token, err = th.App.CreatePasswordRecoveryToken(th.BasicUser.Id, th.BasicUser.Email)
assert.Nil(t, err)
th.App.UpdateConfig(func(c *model.Config) {
*c.EmailSettings.RequireEmailVerification = false
})
th.BasicUser.Email = th.MakeEmail()
_, err = th.App.UpdateUser(th.BasicUser, false)
assert.Nil(t, err)
err = th.App.ResetPasswordFromToken(token.Token, "abcdefgh")
assert.NotNil(t, err)
}
func TestGetViewUsersRestrictions(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
team1 := th.CreateTeam()
team2 := th.CreateTeam()
th.CreateTeam() // Another team
user1 := th.CreateUser()
th.LinkUserToTeam(user1, team1)
th.LinkUserToTeam(user1, team2)
th.App.UpdateTeamMemberRoles(team1.Id, user1.Id, "team_user team_admin")
team1channel1 := th.CreateChannel(team1)
team1channel2 := th.CreateChannel(team1)
th.CreateChannel(team1) // Another channel
team1offtopic, err := th.App.GetChannelByName("off-topic", team1.Id, false)
require.Nil(t, err)
team1townsquare, err := th.App.GetChannelByName("town-square", team1.Id, false)
require.Nil(t, err)
team2channel1 := th.CreateChannel(team2)
th.CreateChannel(team2) // Another channel
team2offtopic, err := th.App.GetChannelByName("off-topic", team2.Id, false)
require.Nil(t, err)
team2townsquare, err := th.App.GetChannelByName("town-square", team2.Id, false)
require.Nil(t, err)
th.App.AddUserToChannel(user1, team1channel1, false)
th.App.AddUserToChannel(user1, team1channel2, false)
th.App.AddUserToChannel(user1, team2channel1, false)
addPermission := func(role *model.Role, permission string) *model.AppError {
newPermissions := append(role.Permissions, permission)
_, err := th.App.PatchRole(role, &model.RolePatch{Permissions: &newPermissions})
return err
}
removePermission := func(role *model.Role, permission string) *model.AppError {
newPermissions := []string{}
for _, oldPermission := range role.Permissions {
if permission != oldPermission {
newPermissions = append(newPermissions, oldPermission)
}
}
_, err := th.App.PatchRole(role, &model.RolePatch{Permissions: &newPermissions})
return err
}
t.Run("VIEW_MEMBERS permission granted at system level", func(t *testing.T) {
restrictions, err := th.App.GetViewUsersRestrictions(user1.Id)
require.Nil(t, err)
assert.Nil(t, restrictions)
})
t.Run("VIEW_MEMBERS permission granted at team level", func(t *testing.T) {
systemUserRole, err := th.App.GetRoleByName(context.Background(), model.SystemUserRoleId)
require.Nil(t, err)
teamUserRole, err := th.App.GetRoleByName(context.Background(), model.TeamUserRoleId)
require.Nil(t, err)
require.Nil(t, removePermission(systemUserRole, model.PermissionViewMembers.Id))
defer addPermission(systemUserRole, model.PermissionViewMembers.Id)
require.Nil(t, addPermission(teamUserRole, model.PermissionViewMembers.Id))
defer removePermission(teamUserRole, model.PermissionViewMembers.Id)
restrictions, err := th.App.GetViewUsersRestrictions(user1.Id)
require.Nil(t, err)
assert.NotNil(t, restrictions)
assert.NotNil(t, restrictions.Teams)
assert.NotNil(t, restrictions.Channels)
assert.ElementsMatch(t, []string{team1townsquare.Id, team1offtopic.Id, team1channel1.Id, team1channel2.Id, team2townsquare.Id, team2offtopic.Id, team2channel1.Id}, restrictions.Channels)
assert.ElementsMatch(t, []string{team1.Id, team2.Id}, restrictions.Teams)
})
t.Run("VIEW_MEMBERS permission not granted at any level", func(t *testing.T) {
systemUserRole, err := th.App.GetRoleByName(context.Background(), model.SystemUserRoleId)
require.Nil(t, err)
require.Nil(t, removePermission(systemUserRole, model.PermissionViewMembers.Id))
defer addPermission(systemUserRole, model.PermissionViewMembers.Id)
restrictions, err := th.App.GetViewUsersRestrictions(user1.Id)
require.Nil(t, err)
assert.NotNil(t, restrictions)
assert.Empty(t, restrictions.Teams)
assert.NotNil(t, restrictions.Channels)
assert.ElementsMatch(t, []string{team1townsquare.Id, team1offtopic.Id, team1channel1.Id, team1channel2.Id, team2townsquare.Id, team2offtopic.Id, team2channel1.Id}, restrictions.Channels)
})
t.Run("VIEW_MEMBERS permission for some teams but not for others", func(t *testing.T) {
systemUserRole, err := th.App.GetRoleByName(context.Background(), model.SystemUserRoleId)
require.Nil(t, err)
teamAdminRole, err := th.App.GetRoleByName(context.Background(), model.TeamAdminRoleId)
require.Nil(t, err)
require.Nil(t, removePermission(systemUserRole, model.PermissionViewMembers.Id))
defer addPermission(systemUserRole, model.PermissionViewMembers.Id)
require.Nil(t, addPermission(teamAdminRole, model.PermissionViewMembers.Id))
defer removePermission(teamAdminRole, model.PermissionViewMembers.Id)
restrictions, err := th.App.GetViewUsersRestrictions(user1.Id)
require.Nil(t, err)
assert.NotNil(t, restrictions)
assert.NotNil(t, restrictions.Teams)
assert.NotNil(t, restrictions.Channels)
assert.ElementsMatch(t, restrictions.Teams, []string{team1.Id})
assert.ElementsMatch(t, []string{team1townsquare.Id, team1offtopic.Id, team1channel1.Id, team1channel2.Id, team2townsquare.Id, team2offtopic.Id, team2channel1.Id}, restrictions.Channels)
})
}
func TestPromoteGuestToUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("Must fail with regular user", func(t *testing.T) {
require.Equal(t, "system_user", th.BasicUser.Roles)
err := th.App.PromoteGuestToUser(th.Context, th.BasicUser, th.BasicUser.Id)
require.Nil(t, err)
user, err := th.App.GetUser(th.BasicUser.Id)
assert.Nil(t, err)
assert.Equal(t, "system_user", user.Roles)
})
t.Run("Must work with guest user without teams or channels", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
err := th.App.PromoteGuestToUser(th.Context, guest, th.BasicUser.Id)
require.Nil(t, err)
guest, err = th.App.GetUser(guest.Id)
assert.Nil(t, err)
assert.Equal(t, "system_user", guest.Roles)
})
t.Run("Must work with guest user with teams but no channels", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
th.LinkUserToTeam(guest, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeGuest)
require.False(t, teamMember.SchemeUser)
err = th.App.PromoteGuestToUser(th.Context, guest, th.BasicUser.Id)
require.Nil(t, err)
guest, err = th.App.GetUser(guest.Id)
assert.Nil(t, err)
assert.Equal(t, "system_user", guest.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeGuest)
assert.True(t, teamMember.SchemeUser)
})
t.Run("Must work with guest user with teams and channels", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
th.LinkUserToTeam(guest, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeGuest)
require.False(t, teamMember.SchemeUser)
channelMember := th.AddUserToChannel(guest, th.BasicChannel)
require.True(t, channelMember.SchemeGuest)
require.False(t, channelMember.SchemeUser)
err = th.App.PromoteGuestToUser(th.Context, guest, th.BasicUser.Id)
require.Nil(t, err)
guest, err = th.App.GetUser(guest.Id)
assert.Nil(t, err)
assert.Equal(t, "system_user", guest.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeGuest)
assert.True(t, teamMember.SchemeUser)
_, err = th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, guest.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeGuest)
assert.True(t, teamMember.SchemeUser)
})
t.Run("Must add the default channels", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
th.LinkUserToTeam(guest, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeGuest)
require.False(t, teamMember.SchemeUser)
channelMember := th.AddUserToChannel(guest, th.BasicChannel)
require.True(t, channelMember.SchemeGuest)
require.False(t, channelMember.SchemeUser)
channelMembers, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
require.Len(t, channelMembers, 1)
err = th.App.PromoteGuestToUser(th.Context, guest, th.BasicUser.Id)
require.Nil(t, err)
guest, err = th.App.GetUser(guest.Id)
assert.Nil(t, err)
assert.Equal(t, "system_user", guest.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeGuest)
assert.True(t, teamMember.SchemeUser)
_, err = th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, guest.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeGuest)
assert.True(t, teamMember.SchemeUser)
channelMembers, err = th.App.GetChannelMembersForUser(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
assert.Len(t, channelMembers, 3)
})
t.Run("Must invalidate channel stats cache when promoting a guest", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
th.LinkUserToTeam(guest, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeGuest)
require.False(t, teamMember.SchemeUser)
guestCount, _ := th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(0), guestCount)
channelMember := th.AddUserToChannel(guest, th.BasicChannel)
require.True(t, channelMember.SchemeGuest)
require.False(t, channelMember.SchemeUser)
guestCount, _ = th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(1), guestCount)
err = th.App.PromoteGuestToUser(th.Context, guest, th.BasicUser.Id)
require.Nil(t, err)
guestCount, _ = th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(0), guestCount)
})
}
func TestDemoteUserToGuest(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("Must invalidate channel stats cache when demoting a user", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
th.LinkUserToTeam(user, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeUser)
require.False(t, teamMember.SchemeGuest)
guestCount, _ := th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(0), guestCount)
channelMember := th.AddUserToChannel(user, th.BasicChannel)
require.True(t, channelMember.SchemeUser)
require.False(t, channelMember.SchemeGuest)
guestCount, _ = th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(0), guestCount)
err = th.App.DemoteUserToGuest(user)
require.Nil(t, err)
guestCount, _ = th.App.GetChannelGuestCount(th.BasicChannel.Id)
require.Equal(t, int64(1), guestCount)
})
t.Run("Must fail with guest user", func(t *testing.T) {
guest := th.CreateGuest()
require.Equal(t, "system_guest", guest.Roles)
err := th.App.DemoteUserToGuest(guest)
require.Nil(t, err)
user, err := th.App.GetUser(guest.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
})
t.Run("Must work with user without teams or channels", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
err := th.App.DemoteUserToGuest(user)
require.Nil(t, err)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
})
t.Run("Must work with user with teams but no channels", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
th.LinkUserToTeam(user, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeUser)
require.False(t, teamMember.SchemeGuest)
err = th.App.DemoteUserToGuest(user)
require.Nil(t, err)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.True(t, teamMember.SchemeGuest)
})
t.Run("Must work with user with teams and channels", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
th.LinkUserToTeam(user, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeUser)
require.False(t, teamMember.SchemeGuest)
channelMember := th.AddUserToChannel(user, th.BasicChannel)
require.True(t, channelMember.SchemeUser)
require.False(t, channelMember.SchemeGuest)
err = th.App.DemoteUserToGuest(user)
require.Nil(t, err)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.True(t, teamMember.SchemeGuest)
_, err = th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.True(t, teamMember.SchemeGuest)
})
t.Run("Must respect the current channels not removing defaults", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
th.LinkUserToTeam(user, th.BasicTeam)
teamMember, err := th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeUser)
require.False(t, teamMember.SchemeGuest)
channelMember := th.AddUserToChannel(user, th.BasicChannel)
require.True(t, channelMember.SchemeUser)
require.False(t, channelMember.SchemeGuest)
channelMembers, err := th.App.GetChannelMembersForUser(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
require.Len(t, channelMembers, 3)
err = th.App.DemoteUserToGuest(user)
require.Nil(t, err)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
teamMember, err = th.App.GetTeamMember(th.BasicTeam.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.True(t, teamMember.SchemeGuest)
_, err = th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.True(t, teamMember.SchemeGuest)
channelMembers, err = th.App.GetChannelMembersForUser(th.BasicTeam.Id, user.Id)
require.Nil(t, err)
assert.Len(t, channelMembers, 3)
})
t.Run("Must be removed as team and channel admin", func(t *testing.T) {
user := th.CreateUser()
require.Equal(t, "system_user", user.Roles)
team := th.CreateTeam()
th.LinkUserToTeam(user, team)
th.App.UpdateTeamMemberRoles(team.Id, user.Id, "team_user team_admin")
teamMember, err := th.App.GetTeamMember(team.Id, user.Id)
require.Nil(t, err)
require.True(t, teamMember.SchemeUser)
require.True(t, teamMember.SchemeAdmin)
require.False(t, teamMember.SchemeGuest)
channel := th.CreateChannel(team)
th.AddUserToChannel(user, channel)
th.App.UpdateChannelMemberSchemeRoles(channel.Id, user.Id, false, true, true)
channelMember, err := th.App.GetChannelMember(context.Background(), channel.Id, user.Id)
assert.Nil(t, err)
assert.True(t, channelMember.SchemeUser)
assert.True(t, channelMember.SchemeAdmin)
assert.False(t, channelMember.SchemeGuest)
err = th.App.DemoteUserToGuest(user)
require.Nil(t, err)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, "system_guest", user.Roles)
teamMember, err = th.App.GetTeamMember(team.Id, user.Id)
assert.Nil(t, err)
assert.False(t, teamMember.SchemeUser)
assert.False(t, teamMember.SchemeAdmin)
assert.True(t, teamMember.SchemeGuest)
channelMember, err = th.App.GetChannelMember(context.Background(), channel.Id, user.Id)
assert.Nil(t, err)
assert.False(t, channelMember.SchemeUser)
assert.False(t, channelMember.SchemeAdmin)
assert.True(t, channelMember.SchemeGuest)
})
}
func TestDeactivateGuests(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
guest1 := th.CreateGuest()
guest2 := th.CreateGuest()
user := th.CreateUser()
err := th.App.DeactivateGuests(th.Context)
require.Nil(t, err)
guest1, err = th.App.GetUser(guest1.Id)
assert.Nil(t, err)
assert.NotEqual(t, int64(0), guest1.DeleteAt)
guest2, err = th.App.GetUser(guest2.Id)
assert.Nil(t, err)
assert.NotEqual(t, int64(0), guest2.DeleteAt)
user, err = th.App.GetUser(user.Id)
assert.Nil(t, err)
assert.Equal(t, int64(0), user.DeleteAt)
}
func TestUpdateUserRolesWithUser(t *testing.T) {
// InitBasic is used to let the first CreateUser call not be
// a system_admin
th := Setup(t).InitBasic()
defer th.TearDown()
// Create normal user.
user := th.CreateUser()
assert.Equal(t, user.Roles, model.SystemUserRoleId)
// Upgrade to sysadmin.
user, err := th.App.UpdateUserRolesWithUser(user, model.SystemUserRoleId+" "+model.SystemAdminRoleId, false)
require.Nil(t, err)
assert.Equal(t, user.Roles, model.SystemUserRoleId+" "+model.SystemAdminRoleId)
// Test bad role.
_, err = th.App.UpdateUserRolesWithUser(user, "does not exist", false)
require.NotNil(t, err)
}
func TestDeactivateMfa(t *testing.T) {
t.Run("MFA is disabled", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.EnableMultifactorAuthentication = false
})
user := th.BasicUser
err := th.App.DeactivateMfa(user.Id)
require.Nil(t, err)
})
}
func TestPatchUser(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
testUser := th.CreateUser()
defer th.App.PermanentDeleteUser(th.Context, testUser)
t.Run("Patch with a username already exists", func(t *testing.T) {
_, err := th.App.PatchUser(testUser.Id, &model.UserPatch{
Username: model.NewString(th.BasicUser.Username),
}, true)
require.NotNil(t, err)
require.Equal(t, "app.user.save.username_exists.app_error", err.Id)
})
t.Run("Patch with a email already exists", func(t *testing.T) {
_, err := th.App.PatchUser(testUser.Id, &model.UserPatch{
Email: model.NewString(th.BasicUser.Email),
}, true)
require.NotNil(t, err)
require.Equal(t, "app.user.save.email_exists.app_error", err.Id)
})
t.Run("Patch username with a new username", func(t *testing.T) {
_, err := th.App.PatchUser(testUser.Id, &model.UserPatch{
Username: model.NewString(model.NewId()),
}, true)
require.Nil(t, err)
})
}
func TestUpdateThreadReadForUser(t *testing.T) {
os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
t.Run("Ensure thread membership is created and followed", func(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
th.App.UpdateConfig(func(cfg *model.Config) {
*cfg.ServiceSettings.ThreadAutoFollow = true
*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
})
rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
require.Nil(t, appErr)
replyPost, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
require.Nil(t, appErr)
threads, appErr := th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
require.Nil(t, appErr)
require.Zero(t, threads.Total)
_, appErr = th.App.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicChannel.TeamId, rootPost.Id, replyPost.CreateAt)
require.Nil(t, appErr)
threads, appErr = th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
require.Nil(t, appErr)
assert.NotZero(t, threads.Total)
threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
require.Nil(t, appErr)
require.NotNil(t, threadMembership)
assert.True(t, threadMembership.Following)
})
t.Run("Ensure no panic on error", func(t *testing.T) {
th := SetupWithStoreMock(t)
defer th.TearDown()
mockStore := th.App.Srv().Store.(*storemocks.Store)
mockUserStore := storemocks.UserStore{}
mockUserStore.On("Count", mock.Anything).Return(int64(10), nil)
mockUserStore.On("Get", mock.Anything, "user1").Return(&model.User{Id: "user1"}, nil)
mockThreadStore := storemocks.ThreadStore{}
mockThreadStore.On("MaintainMembership", "user1", "postid", mock.Anything).Return(nil, errors.New("error"))
var err error
th.App.ch.srv.userService, err = users.New(users.ServiceConfig{
UserStore: &mockUserStore,
SessionStore: &storemocks.SessionStore{},
OAuthStore: &storemocks.OAuthStore{},
ConfigFn: th.App.ch.srv.Config,
LicenseFn: th.App.ch.srv.License,
})
require.NoError(t, err)
mockStore.On("User").Return(&mockUserStore)
mockStore.On("Thread").Return(&mockThreadStore)
_, err = th.App.UpdateThreadReadForUser("user1", "team1", "postid", 100)
require.Error(t, err)
})
}
func TestCreateUserWithInitialPreferences(t *testing.T) {
th := Setup(t).InitBasic()
defer th.TearDown()
t.Run("successfully create a user with initial tutorial and recommended steps preferences", func(t *testing.T) {
testUser := th.CreateUser()
defer th.App.PermanentDeleteUser(th.Context, testUser)
preferences, appErr := th.App.GetPreferencesForUser(testUser.Id)
require.Nil(t, appErr)
tutorialStepPref := preferences[1]
recommendedNextStepsPref := preferences[0]
assert.Equal(t, tutorialStepPref.Name, testUser.Id)
assert.Equal(t, recommendedNextStepsPref.Category, model.PreferenceRecommendedNextSteps)
assert.Equal(t, recommendedNextStepsPref.Name, "hide")
assert.Equal(t, recommendedNextStepsPref.Value, "false")
})
t.Run("successfully create a guest user with initial tutorial and recommended steps preferences", func(t *testing.T) {
testUser := th.CreateGuest()
defer th.App.PermanentDeleteUser(th.Context, testUser)
preferences, appErr := th.App.GetPreferencesForUser(testUser.Id)
require.Nil(t, appErr)
assert.Equal(t, testUser.Id, preferences[0].UserId)
assert.Equal(t, model.PreferenceRecommendedNextSteps, preferences[0].Category)
assert.Equal(t, "hide", preferences[0].Name)
assert.Equal(t, "false", preferences[0].Value)
assert.Equal(t, testUser.Id, preferences[1].UserId)
assert.Equal(t, model.PreferenceCategoryTutorialSteps, preferences[1].Category)
assert.Equal(t, testUser.Id, preferences[1].Name)
assert.Equal(t, "0", preferences[1].Value)
})
}